[Haskell-fr] Éclaircissements sur les monades

Valentin Robert valentin.robert.42 at gmail.com
Wed Mar 26 20:19:19 UTC 2014


Ci-dessous une intuition guidée par les types pour identifier une
différence entre les foncteurs applicatifs et les monades qui a été
mentionée auparavant par Alp.

La composition de foncteurs applicatif a pour type:
c a -> c (a -> b)  -> c b
La composition de monades a pour type:
c a -> (a -> c b) -> c b

(J'ai renommé les variables f et m en c, et réordonné les paramètres pour
simplifier mon explication)

Dans les deux cas, on souhaite applique uniformément une sorte de fonction
(le second paramètre) sur une valeur en contexte (le premier paramètre).

Cependant, le type donné à la fonction lui confère un potentiel différent
dans chaque cas:
- pour Applicative, la fonction qui reçoit un a n'a pas la possibilité
d'altérer son contexte, car la fonction est dans le contexte: c (a -> b)
- pour Monad, la fonction qui reçoit un a se charge de construire le
contexte pour son résultat: a -> c b

Du coup, dans Applicative, la valeur de a peut seulement influencer la
valeur de b, mais pas la structure du contexte c.
Alors que dans Monad, la valeur de a peut influencer à la fois la valeur de
b et la structure du contexte c dans lequel b est placé.

---

Dans le cas des listes :

(<*>) :: [a -> b]  -> [a] -> [b]   -- j'ai remis les paramètres dans
l'ordre d'Haskell pour la suite de mon email
(>>=) :: [a] -> (a -> [b]) -> [b]

Pour (fl <*> xl), quelles que soient les valeurs dans xl, la liste
résultante aura autant d'éléments que le produit du nombre d'éléments de fl
et xl. Les fonctions contenues dans fl peuvent contrôler les valeurs dans
la liste finale, mais pas la structure de la liste finale.

Pour (xl >>= fl), les fonctions peuvent également affecter la structure
résultante.

[1,2,3] >>= (\a -> if a == 1 then [] else [a+1, a+2])

Ici, j'ai le droit de regarder la valeur, et de modifier la structure du
contexte produit (par exemple, je produis une liste vide quand la valeur
est 1, et une liste à deux éléments autrement).

---

Dans la monade IO, c'est ce qui te permet de choisir quelles actions faire
en fonction du résultat produit par les actions précédentes.

(<*>) :: IO (a -> b)  -> IO a -> IO b
(>>=) :: IO a -> (a -> IO b) -> IO b

iof <*> ioa   -- quelle que soit la valeur produite par ioa, on applique la
fonction dans iof
ioa >>= (\a -> ....)   -- ici, dans les ... tu peux regarder a, et
effectuer différentes actions, ce qui est impossible avec Applicative

---

Corrigez-moi si je dis des bêtises... je suis malade et fatigué ! :-)

- Valentin


2014-03-26 11:41 GMT-07:00 Gautier DI FOLCO <gautier.difolco at gmail.com>:

> Le 26 mars 2014 18:04, Sylvain Henry <hsyl20 at gmail.com> a écrit :
>
> Bonjour,
>>
>> J'avais fait un exposé pour expliquer ça entre autres, je ne sais pas si
>> c'est tellement compréhensible sans les explications données à l'oral mais
>> les slides sont là :
>> http://www.sylvain-henry.info/home/data/uploads/talks/shenry-2013-02-05-haskell-intro.pdf(à partir du 20e slide).
>>
>> * Contexte
>>
>> Dans un langage fonctionnel, on n'utilise pas de variable globale donc
>> pour qu'il y ait un contexte il faut le passer explicitement en entrée des
>> fonctions et le renvoyer en sortie (en plus d'autres valeurs
>> potentiellement) :
>> f :: ... -> Context -> (Context, ...)
>>
>> pour ne pas écrire à chaque fois les types "Context", on peut faire :
>> type M a = Context -> (Context, a)
>> et réécrire :
>> f :: ... -> M a
>>
>
> ok, déjà ça c'est plus clair.
>
>
>> Il ne reste plus qu'à définir la composition pour les fonctions de ce
>> type de sorte que le contexte soit passé de la sortie de la première
>> fonction à l'entrée de l'autre :
>> (>>=) :: M a -> (a -> M b) -> M b
>> (>>=) f g = \ctx -> let (ctx2, a) = f ctx in g a ctx2
>>
>
> C'est là que je décroche, après un >>= on a bien une fonction qui attends
> un paramètre ? si oui, qui lui donne ?
>
>
>> Pour créer une fonction de type "M a" à partir d'un "a", on définit la
>> fonction :
>> return :: a -> M a
>> return x = \ctx -> (ctx,x)
>>
>
> idem
>
>
>> * IO
>>
>> Dans le cas des IO, le "contexte" est le "monde" (extérieur au
>> programme). La fonction main a le type suivant :
>> main :: IO ()
>> soit
>> main :: World -> (World, () )
>>
>> Donc on peut composer des fonctions qui ont des effets de bord avec (>>=)
>> de sorte que ce soit correct d'un point de vue types sans jamais dire
>> exactement ce que représente le type World. Les opérations s'exécutent en
>> séquence puisque chaque opération "attend de récupérer le monde renvoyé par
>> l'opération qui la précède" (du moins c'est l'idée). Il n'y a aucun moyen
>> pour le programme de créer un objet de type World donc toutes les
>> opérations s'enchainent forcément à partir de celui passé à "main".
>>
>
> ça ok, mais comment les mondes se "passent" entre les différents appel
> (bien que d'après ce que j'ai pu lire, l'IO monade est un cas particulier).
>
>
>> * Monades
>>
>> Les monades sont juste une généralisation du mécanisme décrit ci-dessus
>> (en gros), c'est pourquoi ils sont adaptés. La typeclass Monad implique la
>> typeclass Applicative (l'inverse n'est pas vrai) et on aurait pu utiliser
>> Applicative à la place de Monad dans le cas des IOs. Ça a même été suggéré
>> sur la mailing-list haskell-cafe il n'y a pas si longtemps si je me
>> souviens bien.
>>
>
> Merci, du coup les Applicative permettent aussi des effets de bords et
> d’enchaînements ?
>
> _______________________________________________
> Haskell-fr mailing list
> Haskell-fr at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-fr
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-fr/attachments/20140326/d3d725d3/attachment.html>


More information about the Haskell-fr mailing list