Si tu as déjà touché à la programmation fonctionnelle, tu as probablement entendu parler de la notion de Monade. Il existe pléthore d'articles sur Internet couvrant les monades, mais beaucoup d'entre eux sont teintés de notions mathématiques, et plus précisément de théorie des catégories.
Pour définir la notion de monade en deux mots : Contexte et Composition.
Les exemples de code qui suivent sont écrits en Haskell, un langage que j'affectionne particulièrement pour sa syntaxe concise, mais très expressive.
Imagine une fonction qui retourne, pour tout entier positif, le double de sa valeur. Comment représenter le fait que cette fonction puisse ne pas retourner de valeur utile, dans le cas où le paramètre est négatif ? Il s'agit là d'un contexte d'absence probable de valeur utile, et il y a une monade pour ça !
Maybe a
Haskell propose un type Maybe a
pour représenter ce contexte, où a
est un type paramétré. Ce type propose deux valeurs, Just a
et Nothing
. Ainsi, le type Maybe Int
contient les valeurs Just Int
et Nothing
, le type Maybe String
contient les valeurs Just String
et Nothing
, etc
Ok, maintenant que nous avons découvert ce contexte, on va l'utiliser pour implémenter notre fonction, qui retournera dans un contexte Just
le double de la valeur, si elle est positive, sinon Nothing
:
Il est d'usage d'utiliser la fonction return
pour placer une valeur dans son contexte, le contexte est inféré grace au prototype de la fonction :
C'est bien d'avoir une valeur contextualisée, mais encore faut il pouvoir l'exploiter.
Usage et composition
Comment faire maintenant pour, par exemple, réappliquer la fonction sur la valeur retournée ?
Le code ci-dessus ne compile pas car il est impossible d'appliquer la fonction doublePositive
à Just Int
(Maybe Int
n'est pas un Int
).
Maybe a
propose la fonction >>=
qui permet de réaliser une composition de valeurs contextualisées. De cette manière, les fonctions a -> M a
et a -> M b
peuvent être composées en une fonction a -> M b
.
Le prototype de la fonction >>=
est M a -> (a -> M b) -> M b
Tu viens d'appliquer la fonction doublePositive
à Just Int
! Tu peux évidemment appliquer toute fonction de type Int -> Maybe Int !
Le prototype de la fonction >>=
étant M a -> (a -> M b) -> M b
, tu peux évidemment transformer ton Int en String, ou en tout autre type !
Les fonctions sont donc maintenant composables les unes avec les autres.
Et la monade dans tout ça ?
Tu viens de voir que Maybe a
est une monade parmi d'autres. Les deux fonctions return
et >>=
que l'on vient d'utiliser sont disponibles pour toutes les monades. Haskell dispose d'un typeclass
Monad
qui t'oblige à définir ces fonctions si tu dérives Monad
(Maybe est une instance de Monad, donc implémente ces fonctions).
Tu dois savoir que pour être une monade, un type doit respecter trois lois :
- Composition neutre par return à gauche :
return x >>= f
est équivalent àf x
- Composition neutre par return à droite :
m >>= return
est équivalent àm
- Associativité :
(m >>= f) >>= g
est équivalent àm >>= (\x -> f x >>= g)
Maybe
couvre ces trois conditions :
Si tu veux en savoir plus sur Maybe, je te conseille de visiter le site Hoogle, et plus particulièrement la page dédiée au type Maybe.