Le besoin de pur dans les applicatifs

19

J'apprends les candidats de Haskell. Il me semble (je me trompe probablement) que la purefonction n'est pas vraiment nécessaire, par exemple:

pure (+) <*> [1,2,3] <*> [3,4,5]

peut être écrit comme

(+) <$> [1,2,3] <*> [3,4,5]

Quelqu'un peut-il expliquer les avantages de la purefonction par rapport à un mappage explicite fmap?

Gil Shafriri
la source
1
Vous avez raison - pure f <*> xc'est exactement la même chose que fmap f x. Je suis sûr qu'il y a une raison pour laquelle cela a pureété inclus Applicative, mais je ne sais pas vraiment pourquoi.
bradrn
4
Je n'ai pas le temps pour une réponse, et je ne suis pas convaincu que cela en ferait une bonne ou complète de toute façon, mais une observation: purepermet d'utiliser, bien, des valeurs "pures" dans un calcul Applicatif. Alors que, comme vous le constatez correctement, pure f <*> xc'est la même chose que f <$> x, par exemple , il n'y a pas d'équivalent pour f <*> x <*> pure y <*> z. (Au moins, je ne pense pas.)
Robin Zigmond
3
Autre justification plus théorique - il existe une formulation alternative qui la relie étroitement à la Monoidclasse importante - dans laquelle purecorrespond l' Monoidélément identitaire de. (Cela suggère que Applicativesans purepourrait être intéressant, car Semigroup- ce qui est Monoidsans nécessairement avoir une identité - est toujours utilisé. En fait, maintenant j'y pense, il me semble que PureScript a exactement une telle pureclasse "Applicative sans ", bien que je ne sais pas à quoi il sert.)
Robin Zigmond
2
@RobinZigmond fmap (\f' x' z' -> f' x' y z') f <*> x <*> z, je pense. L'idée est dans la Applicativedocumentation comme la loi de "l'échange".
HTNW
3
@RobinZigmond Applicativesans pureexiste Applydepuis les semi-groupes .
duplode le

Réponses:

8

Je suis à la limite de mes compétences ici, alors ne prenez pas cela plus qu'il ne l'est, mais c'était un peu trop long pour un commentaire.

Il peut y avoir des raisons pratiques à inclure puredans la classe type, mais de nombreuses abstractions de Haskell sont dérivées de fondements théoriques, et je pense que c'est également le cas Applicative. Comme le dit la documentation, il s'agit d'un foncteur monoïdal laxiste puissant (voir https://cstheory.stackexchange.com/q/12412/56098 pour une élaboration). Je suppose que cela puresert d' identité , tout comme le returnfait pour Monad(qui est un monoïde dans la catégorie des endofoncteurs ).

Considérez pureet liftA2:

pure :: a -> f a
liftA2 :: (a -> b -> c) -> f a -> f b -> f c

Si vous plissez les yeux un peu, vous pouvez peut-être imaginer que liftA2c'est une opération binaire, ce qui est également ce que la documentation indique:

Transformez une fonction binaire en actions.

pureest donc l'identité correspondante.

Mark Seemann
la source
6
Exactement. Applicativesans pureserait un foncteur semi - groupe hm au lieu d'un foncteur monoïde.
leftaroundabout
20

fmapne le coupe pas toujours. Plus précisément, purec'est ce qui vous permet d'introduire f(où fest Applicative) quand vous ne l'avez pas déjà. Un bon exemple est

sequence :: Applicative f => [f a] -> f [a]

Il prend une liste «d'actions» produisant des valeurs et la transforme en une action produisant une liste de valeurs. Que se passe-t-il lorsqu'il n'y a aucune action dans la liste? Le seul résultat sain est une action qui ne produit aucune valeur:

sequence [] = pure [] -- no way to express this with an fmap
-- for completeness
sequence ((:) x xs) = (:) <$> x <*> sequence xs

Si ce n'était pas le cas pure, vous seriez obligé d'exiger une liste d'actions non vide. Vous pourriez certainement le faire fonctionner, mais c'est comme parler d'addition sans mentionner 0 ou de multiplication sans 1 (comme d'autres l'ont dit, car les Applicatives sont monoïdaux). Vous rencontrerez à plusieurs reprises des cas marginaux qui seraient facilement résolus puremais qui devraient être résolus à la place par des restrictions étranges sur vos entrées et autres pansements.

HTNW
la source