Désolé, je ne sais pas vraiment mes calculs, je suis donc curieux de savoir comment prononcer les fonctions dans l'Applicatif typeclass
En sachant que vos maths, ou pas, est largement hors de propos ici, je pense. Comme vous le savez probablement, Haskell emprunte quelques morceaux de terminologie à partir de divers domaines de l'abstrait des mathématiques, notamment la Catégorie de la Théorie, d'où nous obtenons les foncteurs et les monades. L'utilisation de ces termes en Haskell diverge quelque peu de la définition mathématique, mais ils sont généralement assez proches pour être de bons termes descriptifs de toute façon.
L' Applicative
type de la classe se trouve quelque part entre Functor
et Monad
, donc on peut s'attendre à avoir une même base mathématique. La documentation de l' Control.Applicative
module commence par:
Ce module décrit une structure intermédiaire entre un foncteur et une monade: il offre un pur expressions et de séquençage, mais aucune liaison. (Techniquement, une forte lax monoidal foncteur.)
Hmm.
class (Functor f) => StrongLaxMonoidalFunctor f where
. . .
Pas tout à fait aussi accrocheur qu' Monad
, je pense.
Ce que tout cela peut se résumer à l' Applicative
ne correspond pas à une notion qui est particulièrement intéressant sur le plan mathématique, donc il n'y a pas de prêt-à-termes qui traînent qui capturent la façon dont il est utilisé en Haskell. Alors, ensemble les mathématiques de côté pour l'instant.
Si nous voulons savoir ce qu'il faut appeler (<*>)
, il pourrait aider à savoir ce qu'il signifie.
Donc, qu'est-ce qu' Applicative
, de toute façon, et pourquoi ne nous l'appeler?
Ce Applicative
des montants dans la pratique est un moyen de lever arbitraire de fonctions dans un Functor
. Tenir compte de la combinaison de l' Maybe
(sans doute le plus simple non-trivial Functor
) et Bool
(de la même manière la plus simple de non-trivial de type de données).
maybeNot :: Maybe Bool -> Maybe Bool
maybeNot = fmap not
La fonction fmap
nous permet de soulever not
de travail sur Bool
de travail sur Maybe Bool
. Mais que faire si nous voulons soulever (&&)
?
maybeAnd' :: Maybe Bool -> Maybe (Bool -> Bool)
maybeAnd' = fmap (&&)
Eh bien, c'est pas ce que nous souhaitons à tous! En fait, c'est assez inutile. Nous pouvons essayer d'être malin et de se faufiler un autre Bool
en Maybe
par l'arrière...
maybeAnd'' :: Maybe Bool -> Bool -> Maybe Bool
maybeAnd'' x y = fmap ($ y) (fmap (&&) x)
...mais ce n'est pas bon. Pour une chose, c'est mal. Pour une autre chose, c'est laid. Nous pourrions continuer à essayer, mais il s'avère qu'il n'y a aucun moyen de lever une fonction de multiples arguments pour travailler sur un arbitraire Functor
. Ennuyeux!
D'autre part, nous pourrions le faire facilement si nous avons utilisé Maybe
s' Monad
exemple:
maybeAnd :: Maybe Bool -> Maybe Bool -> Maybe Bool
maybeAnd x y = do x' <- x
y' <- y
return (x' && y')
Maintenant, c'est beaucoup de tracas juste pour traduire une simple fonction-qui est pourquoi Control.Monad
fournit une fonction pour le faire automatiquement, liftM2
. Le 2 en son nom fait référence au fait qu'il fonctionne sur des fonctions d'exactement deux arguments; des fonctions similaires existent pour les 3, 4 et 5 de l'argument des fonctions. Ces fonctions sont mieux, mais pas parfait, et en spécifiant le nombre d'arguments est laid et maladroit.
Ce qui nous amène au papier, qui a introduit l'Applicatif de type classe. En cela, les auteurs font essentiellement deux observations:
- De levage multi-argument des fonctions dans un
Functor
est tout à fait naturel de le faire
- Cela n'est pas besoin de toutes les capacités d'un
Monad
La fonction normale de l'application est écrite par simple juxtaposition de termes, afin de faire la "levée de l'application" aussi simple et naturelle que possible, le document présente des opérateurs infixes à l'application, levé en l' Functor
, et un type de classe pour fournir ce qui est nécessaire pour cela.
Ce qui nous amène au point suivant: (<*>)
représente simplement la fonction de demande--alors, pourquoi le prononcer autrement que vous ne le whitespace "juxtaposition" d'opérateur?
Mais si ce n'est pas très satisfaisant, nous pouvons observer que l' Control.Monad
module fournit également une fonction qui fait la même chose pour les monades:
ap :: (Monad m) => m (a -> b) -> m a -> m b
Où ap
est, bien sûr, courte pour "appliquer". Depuis tout Monad
peut Applicative
, et ap
des besoins seulement le sous-ensemble de fonctionnalités présent dans le dernier, on peut dire que si (<*>)
n'étaient pas un opérateur, il doit être appelé ap
.
Nous pouvons également aborder les choses dans l'autre sens. L' Functor
opération de levage est appelé fmap
parce que c'est une généralisation de l' map
opération sur les listes. Ce genre de fonction sur des listes de travail comme (<*>)
? Il y a ce qu' ap
n'sur les listes, bien sûr, mais qui n'est pas particulièrement utile sur son propre.
En fait, il y a peut-être plus naturel de l'interprétation pour les listes. Ce qui vient à l'esprit quand vous regardez à la suite de la signature?
listApply :: [a -> b] -> [a] -> [b]
Il y a quelque chose de tellement tentant sur l'idée de la doublure les listes en parallèle, l'application de chaque fonction dans les premiers à l'élément correspondant de la seconde. Malheureusement pour notre vieil ami, Monad
, cette simple opération viole la monade lois si les listes sont de longueurs différentes. Mais il fait une amende Applicative
, auquel cas (<*>)
devient un moyen de cordes, ensemble, une version généralisée de l' zipWith
, alors peut-être que nous pouvons imaginer en l'appelant fzipWith
?
Cette compression idée en réalité nous apporte plein cercle. Rappelons que les mathématiques trucs plus tôt, à propos de monoidal foncteurs? Comme son nom l'indique, ils sont un moyen de la combinaison de la structure de monoids et foncteurs, qui sont tous deux familiers Haskell des classes de type:
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Monoid a where
mempty :: a
mappend :: a -> a -> a
Quelles en seraient les regarder comme si vous les mettez dans une boîte et le secoua un peu? D' Functor
nous allons garder l'idée d'une structure indépendante de son paramètre de type, et de Monoid
nous allons garder la forme globale de l'fonctions:
class (Functor f) => MonoidalFunctor f where
mfEmpty :: f ?
mfAppend :: f ? -> f ? -> f ?
Nous ne voulons pas de supposer qu'il existe un moyen de créer une véritable "vide" Functor
, et nous ne pouvons pas évoquer une valeur d'un type arbitraire, de sorte que nous allons fixer le type d' mfEmpty
comme f ()
.
Nous ne voulons pas forcer mfAppend
à la nécessité constante d'un paramètre de type, alors maintenant, nous avons ceci:
class (Functor f) => MonoidalFunctor f where
mfEmpty :: f ()
mfAppend :: f a -> f b -> f ?
Quel est le type de résultat pour l' mfAppend
? Nous avons deux types arbitraires nous n'en savons rien, nous n'avons pas beaucoup d'options. La chose la plus sensée est de garder à la fois:
class (Functor f) => MonoidalFunctor f where
mfEmpty :: f ()
mfAppend :: f a -> f b -> f (a, b)
À quel point mfAppend
est maintenant clairement d'une version généralisée de l' zip
sur les listes, et nous pouvons reconstruire Applicative
facilement:
mfPure x = fmap (\() -> x) mfEmpty
mfApply f x = fmap (\(f, x) -> f x) (mfAppend f x)
Cela nous montre aussi qu' pure
est liée à l'identité d'un élément Monoid
, alors que d'autres noms pour, il pourrait être n'importe quoi, ce qui suggère une unité de valeur, une valeur null opération, ou d'une telle.
C'était long, donc, pour résumer:
(<*>)
est juste une modification de la fonction de l'application, vous pouvez donc le lire comme "ap" ou "appliquer", ou éluder complètement la fonction normale de l'application.
(<*>)
environ généralise zipWith
sur les listes, de sorte que vous pouvez le lire comme "zip foncteurs avec", de même pour la lecture de fmap
"carte un foncteur avec".
La première est plus proche de l'intention de l' Applicative
type de classe--comme son nom le suggère--donc, c'est ce que je recommande.
En fait, j'encourage l'utilisation libérale, et de la non-prononciation, le tout relevé d'application des opérateurs:
-
(<$>)
, ce qui soulève un seul argument de la fonction en Functor
-
(<*>)
, chaînes multi-fonction d'argument par l'intermédiaire d'un Applicative
-
(=<<)
, qui lie une fonction qui entre dans l' Monad
sur un calcul
Tous les trois sont, au fond, juste en fonction régulière de l'application, pour pimenter un peu le tout.
6 votes
@J Cooper... pourriez-vous entendre comment nous le prononçons ? :) Vous pourriez vouloir poster une demande sur meta.stackoverflow.com pour une fonction d'enregistrement et de lecture de la voix :).
8 votes
Ça se prononce "Bon sang, ils étaient vraiment à court d'opérateurs, n'est-ce pas ?". Aussi, un bon nom pour
pure
pourrait êtremakeApplicative
.0 votes
@Lirik, Eh bien, je suppose que par prononcer je veux dire "comment tu appelles ce truc" :) @Chuck, affichez votre
pure
suggestion comme une réponse et je vous upvote6 votes
(<*>) est la version Control.Applicative de "ap" de Control.Monad, donc "ap" est probablement le nom le plus approprié.
12 votes
J'appellerais ça un cyclope, mais c'est juste moi.
0 votes
Voir aussi stackoverflow.com/questions/7746894/
0 votes
Cet article appelle
<*>
"vaisseau" :) blog.ssanj.net/posts/2014-08-10-boosting-liftA2.html