Introduction
Nous allons voir ensemble les monades. Nous allons voir la monade Identity. elle n’est pas très utile mais nécessaire si vous voulez comprendre la monade/functor Maybe qui j’espère va changer votre façon de voir votre code mais ce sera dans le post suivant.
Les monades sont des structures de la programmation fonctionnelle. Très utilisées dans le langage Haskell. En pratique Haskell serait moins attractifs sans cette structure. (Je ne suis absolument pas développeur Haskell.)
Je ne sais pas trop les définir puisque il existe un nombre incalculable de définitions
- C’est un triplet d’après wikipédia en français
- Une Interface, de l’injection de dépendances, Structure, Une base spatiale, Un burrito
- Des catégories
Il existe une infinité de tutoriels dessus (Le site officiel de Haskell à un compteur plutôt amusant pour quantifier l’avalanche de tuto), écris par les plus grands Douglas Crowford Youtube (La référence du Javascript). Donc probablement que mon explication ne sera pas forcément la meilleure.
Pour comprendre les monades je vais vous parler de container (Rien à voir avec docker, ni container de Symfony).
des valeurs sympas et pas sympa.
J’ai des valeurs sympas, et des valeurs pas sympas.
Par pas sympa, j’entends toute les valeurs que je ne maitrise pas trop par exemple
- la variable n’est pas instanciée le fabuleux
Null
- le résultat n’est pas forcément le même. je lance la fonction deux fois, je n’ai pas le même résultat.
- le résultat dépend d’autre chose, par exemple la lecture d’un fichier (le réseau est coupé, le disque dur est plein, etc ..) et peux entrainer des erreurs et des exceptions.
- le résultat n’a pas forcement la même taille. je pense à un résultat de base de donnée, je peux avoir 0 lignes commes des milliards.
- enfin le résultat utilise des ressources qui sont partagés avec d’autre programme.
La solution le container
La solution :
utiliser un container ou un emballage
L’idée est simple, je mets en quarantaine ma valeur.
Ainsi je suis protégé des effets néfastes.
Voici le début de l’implémentation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
J’ai deux méthodes: un constructeur, et une factory statique. deux possibilités
1 2 3 |
|
1 2 3 4 5 6 7 8 |
|
Ma valeur est dans un container, la propriété est protected
. Donc impossible à atteindre de l’extérieur, à priori on ne craint pas grand chose..
Mais voila mon container aussi sécure qu’il soit ne sert à rien. Puisque rien ne sort, mais rien de rentre..
Un Sas de décontamination.
Je vais ajouter un sas de décontamination à ma structure via l’instruction map
qui prend une fonction. Il applique la fonction à la valeur à l’intérieur. Il a une petite particularité. Il ne donne pas le résultat mais un nouveau container qui contient le résultat.
Soit la fonction suivante qui ajoute 1 à la valeur en entrée.
1 2 3 |
|
Regardons le dessin suivant:
- Je crée un container qui contient la valeur “5”.
- Je mets la fonction
addOne
dans lemap
. Je fais le calcul. Que je m’empresse de remettre dans un container tout neuf. - j’ai un Container avec “6”.
Voici l’implémentation de map
dans ma classe container.
1 2 3 4 5 |
|
Et le code d’exemple.
1 2 3 |
|
Et le résultat
1 2 3 4 |
|
Quelques remarques
- Comme le résultat n’est pas sur, Je remet le résultat dans un nouveau container. Je ne réutilise plus l’ancien container (puisque contaminé). Comme on ne peux changer le contenu, il est immutable
- Le container avec l’instruction
map
par définition Chainable.
1 2 3 4 5 6 7 8 9 10 11 |
|
Bien sur il est parfaitement possible d’utiliser des callbacks
1 2 3 4 5 6 7 8 9 10 |
|
Donc j’ai un Sas d’entrée qui me permet d’interagir avec ma valeur. Je n’ai toujours pas fais sortir la fonction.
Une sortie.
C’est pas très spectaculaire, j’ajoute une fonction extract() qui n’est qu’un simple return
1 2 3 4 5 6 |
|
Exemple
1 2 3 4 5 |
|
Une application : Le décorateur de texte.
Nous allons utiliser la capacité de chainage de notre container pour faire un pseudo-décorateur.
Soit les fonctions suivantes
1 2 3 4 5 6 7 8 9 10 11 |
|
Voici le fonctionnement
- je supprime les espaces en trop avec trim
- Je code en Html htmlentities
- j’encadre de “h1” puis “body” puis “html”.
Le résultat
1
|
|
En image
Une autre idée
Nous pouvons aussi imagine une fonction qui renvoie un Container.
Par exemple reprenons notre fonction addOne
1 2 3 |
|
Donc ma fonction me renvoie forcement un container.
Si j’utilise l’instruction map
, je risque de mettre un container dans le container.
D’où l’ajout de la méthode bind
1 2 3 4 |
|
On remarque que mon résultat reste chaînable.
1 2 3 4 5 6 7 8 9 10 11 |
|
Conclusion
Mon container bien que pour le moment est assez peu utilise mais.
Il implémente une fonction
map
qui est chainable. Nous venons d’implémenter un functor ou foncteur en français. Cela a un rapport avec les mathématiques. Et il m’est difficile au moment ou j’écris ces lignes de vous l’expliquer. Le Functor s’occupe d’appeler la fonction pour nous et de retourner un résultat correct. Il s’occupe de tout. C’est une sorte d’abstraction. On lui confie le calcul et il se débrouille. (Nous le retrouverons dans le post suivant)Nous implémentons la méthode
of
etbind
qui est elle aussi chainable (à condition de lui donner des fonctions qui renvoient des Containers). Nous venons d’implémenter une monade même principe que le functor.
Si vous avez compris le container, vous pouvez le renommer en IdentityMonad.
Dans le prochain post nous allons implémenter un la Monade/Functor Maybe.
Elle nous permettra de réfactoriser le code suivant
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Pour devenir
1 2 3 4 |
|
Je me suis lancé dans une tache bien compliquée mais passionnante. Je m’excuse d’avance pour certaines approximations. J’avais confondu map
et bind
dans la première version
Je vous remercie de m’avoir lu.. * Partie 1 : Monade/Functor * Partie 2 : Le functor Maybe * Partie 3 : Le functor Maybe avec le Bind * Partie 4 : Les listes * Interlude : Les évaluations partielles * Partie 5 : Les applicatives * Partie 6 : Les applicatives et les listes