112 votes

Comment s'appelle <*> et que fait-il ?

Comment fonctionnent ces fonctions dans la classe de type Applicative ?

(<*>) :: f (a -> b) -> f a -> f b
(*>)  :: f a -> f b -> f b
(<*)  :: f a -> f b -> f a

(En effet, s'ils n'étaient pas des opérateurs, comment pourraient-ils s'appeler ?).

En passant, si vous pouviez renommer pure en quelque chose de plus convivial pour les non-mathématiciens, comment l'appellerais-tu ?

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 être makeApplicative .

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 upvote

250voto

C. A. McCann Points 56834

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é Maybes' 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

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

@Colin Cochrane : Tu es sûr que tu n'as pas mal orthographié "long-winded" ? :) Mais bon, je suis d'accord ! J'ai toujours l'impression que Applicative et le style idiomatique fonctionnel qu'il promeut ne reçoivent pas assez d'amour, je n'ai donc pas pu résister à l'occasion de vanter un peu ses vertus comme moyen d'expliquer comment je prononce (ne prononce pas) (<*>) .

6 votes

Si seulement Haskell disposait d'un sucre syntaxique pour Applicative 's ! Quelque chose comme [| f a b c d |] (comme le suggère l'article original). Nous n'aurions alors pas besoin de la <*> combinateur et vous feriez référence à une telle expression comme un exemple d'"application de fonction dans un contexte functoriel".

0 votes

"Pas tout à fait aussi accrocheur que Monad "devrait être "Pas tout à fait aussi accrocheur que Applicative ", n'est-ce pas ?

22voto

BMeph Points 907

Comme je n'ai pas l'intention d'améliorer C. Réponse technique de A. McCann je vais m'attaquer à celui qui est le plus duveteux :

Si vous pouviez renommer pure à quelque chose de plus convivial pour les podunks comme moi, comment l'appelleriez-vous ?

Comme alternative, d'autant plus qu'il n'y a pas de fin à l'angoisse constante et à la trahison des critiques contre l'Union européenne. Monad version, appelée " return ", je propose un autre nom, qui suggère sa fonction d'une manière qui peut satisfaire le plus impératif des programmeurs impératifs, et le plus fonctionnel des... eh bien, espérons-le, tout le monde peut se plaindre de la même chose : inject .

Prenez une valeur. "Injectez" la dans le Functor , Applicative , Monad ou ce que vous voulez. Je vote pour " inject ", et j'ai approuvé ce message.

4 votes

Je penche habituellement pour quelque chose comme "unit" ou "lift", mais ceux-ci ont déjà trop d'autres significations en Haskell. inject est un excellent nom et probablement meilleur que le mien, bien que, comme une note secondaire mineure, "inject" est utilisé dans - je pense - Smalltalk et Ruby pour une méthode de pliage à gauche de quelque sorte. Je n'ai jamais compris ce choix de nom, cependant...

0 votes

@C.A.McCann Smalltalk a obtenu ce nom à partir de une chanson d'Arlo Guthrie à propos de la conscription pour la guerre du Vietnam, dans laquelle de malheureux jeunes hommes étaient rassemblés, sélectionnés, parfois rejetés, et autrement, injectés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X