Haskell Monade lier opérateur de confusion
Ok, donc je ne suis pas un Haskell programmeur, mais je suis absolument fascinée par beaucoup d'idées derrière Haskell et suis à la recherche dans l'apprentissage. Mais je suis coincé à la case départ: je n'arrive pas à envelopper ma tête autour de Monades, qui semblent être assez fondamental. Je sais qu'il y a un million de questions sur DONC demandant d'expliquer les Monades, donc je vais être un peu plus précis sur ce qui m'énerve:
J'ai lu cet excellent article (une introduction à Javascript), et de la pensée que j'ai compris Monades complètement. Puis j'ai lu l'article de Wikipédia sur les Monades, et vu ce:
Une opération de liaison de type polymorphe (M, t)→(t→M u)→(M, u), qui Haskell représente par l'opérateur infixe >>=. Son premier argument est une valeur dans un type monadique, son second argument est une fonction qui cartes du type sous-jacent du premier argument à un autre type monadique, et son résultat est dans cet autre type monadique.
Bon, dans l'article que je cite, de se lier a une fonction qui s' un seul argument. Wikipédia dit que deux. Ce que je pensée j'ai compris sur les Monades était la suivante:
- Une Monade but est de prendre une fonction avec des entrées et des types de sortie et de le rendre modulable. Il le fait en les enveloppant l'entrée et la sortie des types avec un seul type monadique.
- Une Monade se compose de deux fonctions interdépendantes: bind et de l'unité. Lier prend un non-composable fonction f et retourne une nouvelle fonction g qui accepte les monadique type en entrée et renvoie le type monadique. g est modulable. L'unité de la fonction prend un argument de type f prévu, et l'enveloppe dans le type monadique. Cela peut ensuite être transmis à g, ou à toute composition de fonctions comme le g.
Mais il doit y avoir quelque chose de mal, parce que mon concept de bind prend un argument: une fonction. Mais (selon Wikipedia) Haskell lier prend réellement deux arguments! Où est mon erreur?
- Pensez à ce qui arrive si vous inversez l'ordre des arguments de
>>=
et de le traiter comme un curry de fonction.=<<
prend une fonctionf
, et retourne une fonction(f =<<)
qui prend monadique de la valeur... - Les monades ne sont pas fondamentaux.
- n'est-ce pas plus une application partielle plutôt que de nourrissage?
- nourrissage est le nom de la transformation qui permet à une application partielle. Ils sont la même chose. En Haskell il est d'usage d'écrire les fonctions de plusieurs arguments dans le curry de forme, de sorte que vous n'avez pas l'habitude de l'avis de cette équivalence.
- Les monades sont plus comme des carrés de 14 (Real World Haskell, chapitre 14 book.realworldhaskell.org/read)
- le "bind" de l'option Javascript de votre article est
=<<
, qui a le type(a -> m b) -> (m a -> m b)
, il est en effet de transformer un "non-composable"a -> m b
fonction dans un "modulable"m a -> m b
fonction. Nous pouvons dire que les Monades sont sur le "généralisée" de la fonction de demande (m a -> m b
v réguliersa -> b
). Bien sûr, il sont des Foncteurs (fmap
faitm a -> m b
fonctions de trop) et les Foncteurs Applicatifs (<*>
fait la même chose). - Mais les trois diffèrent en ce qu'ils créent ces "composable monadique fonctions" de. ---- "Cela peut ensuite être transmis à
g
" de ne pasg
, mais àbind f
:bind f (unit a) = f a
. Ou en Haskellf =<< return a = return a >>= f = f a
.g
s'attend à ce que le typef a
produit.
Vous devez vous connecter pour publier un commentaire.
Vous n'êtes pas faire une erreur. L'idée-clé de comprendre ici est de lancer une Haskell fonction de deux arguments peut être vu de deux manières. La première est simplement une fonction de deux arguments. Si vous avez, par exemple,
(+)
, ceci est généralement considérée comme prenant deux arguments et de les ajouter. L'autre façon de le voir est comme une addition machine producteur.(+)
est une fonction qui prend un nombre, direx
, et fait une fonction qui va ajouterx
.Lorsque vous traitez avec des monades, parfois, il est probablement mieux, comme ephemient mentionné ci-dessus, à penser à des
=<<
, le retourné de la version de>>=
. Il y a deux façons de regarder ceci:qui est une fonction de deux arguments, et
qui transforme la fonction d'entrée à celui d'un composé de la version que l'article mentionné. Ce sont les équivalents comme
(+)
comme je l'ai expliqué avant.x >>= f
(ouf =<< x
) peut être comprise comme signifiant: prendre f, enveloppez-le de sorte qu'il accepte un type monadique, et ensuite appeler le enveloppés de fonction avec l'argument x?Maybe
,[]
, etIdentity
, ne déballer les valeurs et de les appliquer. Cependant, vous ne pouvez pas vous déballer tout ceNothing
ou[]
. Fondamentalement, le déballage est généralement ce qui est fait, mais il y a très peu d'exceptions mineures.f =<< x
) peut être comprise comme signifiant: prendref
, enveloppez-le de sorte qu'il accepte un type monadique, et ensuite appeler le enveloppés fonction avec l'argumentx
?" non, non. lex
, de typem a
, est "enveloppé" de la valeur, ou il est préférable de dire "una
-type de la valeur dansm
-type de contexte".f
est juste une fonction, s'attend à un "simple"a
-type valeur qui est "enveloppé dans" c'est à dire "dans"m
de type "contexte". Doncbind f
fait quef
de travailler sur cette valeur sur "l'intérieur", ce qui est impossible à obtenir.Me permettre d'abattre vos croyances au sujet de Monades. J'espère sincèrement que vous vous rendez compte que je n'essaie pas d'être impoli; je suis tout simplement en essayant d'éviter de mâcher mes mots.
Pas exactement. Lorsque vous commencez une phrase par "Une Monade but", vous êtes déjà sur le mauvais pied. Les monades n'ont pas nécessairement un "but".
Monad
est tout simplement une abstraction, une classification qui s'applique à certains types et pas à d'autres. Le but de laMonad
abstraction est tout simplement que, abstraction.Oui et non. La combinaison de
bind
etunit
sont suffisantes pour définir une Monade, mais la combinaison dejoin
,fmap
, etunit
est également suffisante. Ce dernier est, en fait, la façon dont les Monades sont généralement décrits dans la Catégorie de la Théorie.Encore, pas exactement. Une fonction monadique
f :: a -> m b
est parfaitement modulable, avec certains types. Je peux poster-composer avec une fonctiong :: m b -> c
pour obtenirg . f :: a -> c
, ou je peux pré-composer avec une fonctionh :: c -> a
pour obtenirf . h :: c -> m b
.Mais vous avez obtenu la deuxième partie tout à fait raison:
(>>= f) :: m a -> m b
. Comme d'autres l'ont noté, Haskellbind
fonction prend les arguments dans l'ordre inverse.Bien, oui. Si
g :: m a -> m b
, alors vous pouvez pré-composer avec une fonctionf :: c -> m a
pour obtenirg . f :: c -> m b
, ou vous pouvez poster-composer avec une fonctionh :: m b -> c
pour obtenirh . g :: m a -> c
. Notez quec
pourrait être de la formem v
oùm
est une Monade. Je suppose que quand vous dites "modulable" tu veux dire "vous pouvez composer arbitrairement longues chaînes de fonctions de cette forme", qui est un peu vrai.Une façon détournée de le dire, mais oui, c'est sur la droite.
Encore une fois, oui. Bien qu'il soit généralement pas idiomatiques Haskell pour appeler
unit
(ou en Haskell,return
) et de passer ensuite que(>>= f)
.g :: m a -> m b
, pourquoi ne pas simplement utiliserf :: a -> b
? Je ne peux pas composer g avec d'autres fonctions comme la signature. Alors quels sont les autres objectifs de monades qui ferait g plus utile que f?return x = [x]
;xs >>= f = concatMap f xs
. (Attention: d'autres, peut-être déroutant rêveries suivre) concatMap cartes une fonctiona -> [b]
sur une liste dea
, et puis "s'aplatit" la liste de[[b]]
à[b]
.concatMap f xs = concat (map f xs)
. Ce n'est (n'est pas un hasard) similaire à la façon dontbind
peut être défini en termes defmap
etjoin
pour aucun monade:x >>= f = join (fmap f x)
; vous pouvez voir quejoin = concat
etfmap = map
pour les listes.L'article que vous lien est basé sur sigfpe de l'article, qui utilise une flippée de la définition de bind:
Donc, le Haskell
bind
prend une valeur comprise dans une monade, et retourne une fonction qui prend une fonction et l'appelle avec la valeur extraite. Je suis peut-être à l'aide de non-précision terminologique, donc peut-être mieux avec code.Vous avez:
Maintenant, la traduction de vos JS bind (Haskell n'automatique de nourrissage, afin de l'appelant
bind f
retourne une fonction qui prend un n-uplet, et puis la correspondance de modèle prend soin de le décompresser dansx
ets
, j'espère que c'est compréhensible):Vous pouvez le constater:
Maintenant, nous allons inverser les arguments de
bind
:Vous pouvez clairement voir qu'elle est encore en train de faire la même chose, mais avec un peu différente de la syntaxe:
Maintenant, Haskell a une syntaxe astuce qui vous permet d'utiliser n'importe quelle fonction comme un opérateur infixe. Vous pouvez donc écrire:
Maintenant renommer
bind'
à>>=
((3, "") >>= cube >>= sine
) et vous avez ce que vous cherchez. Comme vous pouvez le voir, avec cette définition, vous pouvez se débarrasser efficacement de la composition de l'opérateur.La traduction de la nouvelle chose de nouveau dans JavaScript donnerait quelque chose comme ceci (notez qu'encore une fois, je ne inverser l'argument de la commande):
Espère que cette aide, et pas introduit de la confusion — le point est que lier ces deux définitions sont équivalentes, seulement différente de la syntaxe d'appel.
g.mBind(f.mBind(mv))
, comparable àg =<< f =<< mv
. Plus belle peut-être de le définir dans le prototype de la valeur monadique:mv.bind(f).bind(g)
, comparable àmv >>= f >>= g
. Remarque la Fonction de prototype, apparemment, a déjà "lier", défini comme quelque chose.