Étant donné :
Applicative m, Monad m => mf :: m (a -> b), ma :: m a
il semble être considéré comme une loi que :
mf <*> ma === do { f <- mf; a <- ma; return (f a) }
ou de manière plus concise :
(<*>) === ap
Le site la documentation pour Control.Applicative
dit que <*>
est "application séquentielle", et cela suggère que (<*>) = ap
. Cela signifie que <*>
doit évaluer les effets de manière séquentielle, de gauche à droite, pour des raisons de cohérence avec les règles de l'UE. >>=
... Mais ça ne va pas. L'article original de McBride et Paterson semble impliquer que le séquençage de gauche à droite est arbitraire :
La monade IO, et d'ailleurs toute monade, peut être rendue Applicative en prenant
pure
=return
et<*>
=ap
. Nous pourrions également utiliser la variante deap
qui effectue les calculs dans l'ordre inverse mais nous nous en tiendrons à l'ordre de gauche à droite dans ce document.
Il y a donc deux des dérivations légales, non triviales pour <*>
qui découlent de >>=
et return
avec un comportement distinct. Et dans certains cas, ni de ces deux dérivations sont souhaitables.
Par exemple, le (<*>) === ap
forces de l'ordre Validation des données pour définir deux types de données distincts : Validation
et AccValidation
. Le premier a un Monad
instance similaire à SaufT et un Applicative
qui est d'une utilité limitée, puisqu'elle s'arrête après la première erreur. Cette dernière, par contre, ne définit pas une instance de Monad
et est donc libre d'implémenter une instance Applicative
qui, bien plus utilement, accumule les erreurs.
Il y a eu quelques discussion à ce sujet précédemment sur StackOverflow, mais je ne pense pas qu'il ait vraiment répondu à la question :
Pourquoi cela devrait-il être une loi ?
Les autres lois relatives aux foncteurs, aux applicatifs et aux monades - telles que l'identité, l'associativité, etc. - expriment certaines propriétés mathématiques fondamentales de ces structures. Nous pouvons mettre en œuvre diverses optimisations en utilisant ces lois et prouver des choses sur notre propre code en les utilisant. En revanche, j'ai l'impression que les (<*>) === ap
La loi impose une contrainte arbitraire sans avantage correspondant.
Pour ce que ça vaut, je préférerais abandonner la loi en faveur de quelque chose comme ça :
newtype LeftA m a = LeftA (m a)
instance Monad m => Applicative (LeftA m) where
pure = return
mf <*> ma = do { f <- mf; a <- ma; return (f a) }
newtype RightA m a = RightA (m a)
instance Monad m => Applicative (RightA m) where
pure = return
mf <*> ma = do { a <- ma; f <- mf; return (f a) }
Je pense que cela rend correctement la relation entre les deux, sans contraindre indûment l'un ou l'autre.
Donc, quelques angles pour aborder la question :
- Y a-t-il d'autres lois relatives
Monad
etApplicative
? - Y a-t-il une raison mathématique inhérente à l'enchaînement des effets pour
Applicative
de la même manière qu'ils le font pourMonad
? - Est-ce que GHC ou tout autre outil effectue des transformations de code qui supposent/exigent que cette loi soit vraie ?
- Pourquoi le Monade-foncteur-applicative La proposition de la Commission européenne est-elle considérée comme une bonne chose ? (Les citations seraient très appréciées ici).
Et une question bonus :
- Comment
Alternative
etMonadPlus
dans tout ça ?
Note : modification majeure pour clarifier l'essentiel de la question. La réponse postée par @duplode cite une version antérieure.