Comprendre les foncteurs, foncteurs applicatifs et monades avec TypeScript
(evan-moon.github.io)Texte principal
- Explication, à l’aide de code TypeScript, du chemin qui mène des deux problèmes que le seul
mapdes foncteurs ne peut pas résoudre (le problème des fonctions piégées dans un conteneur et le problème de l’imbrication des contextes lors de la composition) jusqu’aux foncteurs applicatifs et aux monades - Le propos part du contexte dans lequel, en 1988, Eugenio Moggi a modélisé les programmes non pas comme
A → B, mais commeA → T(B) - Présentation de la structure
flatMap = map + joinet des trois lois permettant de l’utiliser en toute sûreté (associativité, identité à gauche, identité à droite) - Explication de la raison pour laquelle une monade est « un objet monoïde dans la catégorie des endofoncteurs », en la comparant au monoïde de l’addition des entiers
- Mention également de la raison pour laquelle
Promisefonctionne de manière monadique sans pour autant être une monade mathématique au sens strict
Les limites des foncteurs : ce que map ne permet pas de faire
- Si l’on applique une fonction curryfiée avec
map, le résultat prend la forme deMaybe<(b: number) => number>, et la fonction reste piégée à l’intérieur du conteneurmapne peut recevoir que des fonctions extérieures au conteneur ; il n’existe donc aucun moyen d’appliquer à une autre valeur une fonction enfermée à l’intérieur
- Si l’on compose deux fonctions qui retournent des foncteurs, le contexte s’imbrique sous une forme comme
Maybe<Maybe>- À mesure que les étapes s’accumulent, on obtient une imbrication infinie du type
Maybe<Maybe<Maybe<...>>>
- À mesure que les étapes s’accumulent, on obtient une imbrication infinie du type
Foncteur applicatif : appliquer une fonction à l’intérieur d’un conteneur
- L’opération
applypermet d’appliquer une fonction enfermée dans un conteneur à la valeur d’un autre conteneurapply: T<(A → B)> → T<A> → T<B>
- L’opération
purepermet d’insérer une valeur pure dans un conteneur - Limite : il faut savoir à l’avance quels conteneurs seront composés
- Impossible d’exprimer une dépendance séquentielle dynamique où le calcul suivant est déterminé à partir du résultat du calcul précédent
Monade : l’invention d’une opération qui aplatit l’imbrication
- L’opération
join, de typeT<T<A>> → T<A>, aplatit un double conteneur en un seulArray.prototype.flaten JavaScript joue le même rôle
- En pratique, on utilise
flatMap, qui combinemapetjoinflatMap: T<A> → (A → T<B>) → T<B>- Alors que
mapreçoitA → B,flatMapreçoitA → T<B>, ce qui permet de conserver un seul niveau dans le résultat
Les trois lois de flatMap
- Loi d’associativité : lors du dépliage d’une triple imbrication
T(T(T(A))), le résultat doit être identique qu’on aplatisse d’abord l’intérieur ou l’extérieurm.flatMap(f).flatMap(g) === m.flatMap(x => f(x).flatMap(g))
- Loi d’identité à gauche : si l’on injecte une valeur avec
purepuis qu’on applique immédiatementflatMap, le résultat est identique à l’application directe de la fonctionpure(a).flatMap(f) === f(a)
- Loi d’identité à droite : si l’on passe
pureàflatMap, on retrouve le conteneur d’originem.flatMap(pure) === m
Décomposition de « l’objet monoïde dans la catégorie des endofoncteurs »
- En programmation, les foncteurs vont du monde des types vers le monde des types ; ce sont donc des endofoncteurs
- On peut construire une catégorie des endofoncteurs, dont les objets sont les endofoncteurs eux-mêmes
- Si l’on applique les conditions d’un monoïde (opération binaire + loi d’associativité + élément neutre), on obtient :
- opération binaire =
join - élément neutre =
pure - la structure correspond exactement à celle du monoïde de l’addition des entiers
- opération binaire =
Pourquoi Promise n’est pas une monade
thenmélange le comportement demapet deflatMapselon la valeur de retour- Un état
Promise<Promise>n’est pas autorisé à l’exécution et est immédiatement fusionné en une seule couche - C’est pratique en développement, mais cela ne satisfait pas les lois d’une monade mathématique
Aucun commentaire pour le moment.