├── .github └── FUNDING.yml ├── .editorconfig ├── LICENSE ├── contributing.md └── readme.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: hemanth 4 | patreon: fpjargon 5 | custom: https://paypal.me/gnumanth 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = 120 10 | tab_width = 2 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Hemanth.HM 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contribuer 2 | 3 | Les contributions sont les bienvenues. Néanmoins, ce dépôt ne se veut être qu’une traduction de ce qui peut être trouvé 4 | sur [hemanth/functional-programming-jargon](https://github.com/hemanth/functional-programming-jargon/), donc seules les 5 | améliorations de traduction seront acceptées. Les changements majeurs (par exemple les nouvelles définitions) doivent au 6 | préalable être intégrées à 7 | [hemanth/functional-programming-jargon](https://github.com/hemanth/functional-programming-jargon/blob/master/contributing.md) 8 | avant d’être reprises sur ce dépôt. 9 | 10 | Le contenu de ce dépôt se base sur le commit 11 | [hemanth/functional-programming-jargon@50722a4](https://github.com/hemanth/functional-programming-jargon/commit/50722a4a74cd741b9b03e1807c62a79e5674d4ca) 12 | . 13 | 14 | # Conventions 15 | 16 | - Les blocs de code doivent être également francisés. 17 | - Les acronymes doivent être francisés ou supprimés. Un acronyme anglais a peu de sens en français. 18 | - Les liens doivent être remplacés par des liens équivalent en français ou suffixés avec `(en)`. On pourra 19 | éventuellement compléter les liens avec des liens en français. 20 | - Les termes ou expressions qui n’ont pas d’équivalents en français ne seront pas traduits. 21 | - Les termes en anglais doivent être ajoutés en complément des termes français. Une grande partie de la literature 22 | informatique est en anglais, donc il est utile de connaitre les termes dans les deux langues. 23 | - Les documents markdown doivent être formatés selon ce qui est indiqué dans le fichier [EditorConfig](/.editorconfig). 24 | - Les apostrophes typographiques (`’`) sont préférées aux apostrophe dactylographique (`'`). 25 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Le jargon de la programmation fonctionnelle 2 | 3 | > Ce document est une traduction française de 4 | > [hemanth/functional-programming-jargon](https://github.com/hemanth/functional-programming-jargon) 5 | > basée sur le commit [ce4cc3e](https://github.com/hemanth/functional-programming-jargon/commit/08a48ff). 6 | 7 | La programmation fonctionnelle offre de nombreux avantages et sa popularité n’a cessé d’augmenter en conséquence. 8 | Cependant, chaque paradigme de programmation défini son propre jargon et la programmation fonctionnelle ne fait pas 9 | exception. En fournissant un glossaire, nous espérons faciliter l’apprentissage de la programmation fonctionnelle. 10 | 11 | Les exemples sont écrits en JavaScript (ES2015). 12 | [Pourquoi JavaScript ?](https://github.com/hemanth/functional-programming-jargon/wiki/Why-JavaScript%3F) 13 | 14 | Lorsque cela est possible, ce document utilise les termes définis dans la 15 | [spécification Fantasy Land](https://github.com/fantasyland/fantasy-land). 16 | 17 | __Traductions__ 18 | 19 | * [Portugaise](https://github.com/alexmoreno/jargoes-programacao-funcional) 20 | * [Espagnole](https://github.com/idcmardelplata/functional-programming-jargon/tree/master) 21 | * [Chinoise](https://github.com/shfshanyue/fp-jargon-zh) 22 | * [Indonésienne](https://github.com/wisn/jargon-pemrograman-fungsional) 23 | * [Anglaise (exemples en Python)](https://github.com/jmesyou/functional-programming-jargon.py) 24 | * [Anglaise (exemples en Scala)](https://github.com/ikhoon/functional-programming-jargon.scala) 25 | * [Anglaise (exemples en Rust)](https://github.com/JasonShin/functional-programming-jargon.rs) 26 | * [Coréenne](https://github.com/sphilee/functional-programming-jargon) 27 | * [Polonaise](https://github.com/Deloryn/functional-programming-jargon) 28 | * [Turque (exemples en Haskell)](https://github.com/mrtkp9993/functional-programming-jargon) 29 | * [Russe (exemples en Haskell)](https://github.com/epogrebnyak/functional-programming-jargon) 30 | * [Anglaise (exemples en Julia)](https://github.com/Moelf/functional-programming-jargon.jl) 31 | 32 | __Table des matières__ 33 | 34 | 35 | * [Arité](#arité) 36 | * [Fonction d’ordre supérieur](#fonction-dordre-supérieur) 37 | * [Fermeture](#fermeture) 38 | * [Application partielle](#application-partielle) 39 | * [Curryfication](#curryfication) 40 | * [Auto-curryfication](#auto-curryfication) 41 | * [Composition de fonctions](#composition-de-fonctions) 42 | * [Continuation](#continuation) 43 | * [Fonction pure](#fonction-pure) 44 | * [Effets de bord](#effets-de-bord) 45 | * [Idempotence](#idempotence) 46 | * [Programmation tacite](#programmation-tacite) 47 | * [Prédicat](#prédicat) 48 | * [Contrats](#contrats) 49 | * [Catégorie](#catégorie) 50 | * [Valeur](#valeur) 51 | * [Constante](#constante) 52 | * [Fonction constante](#fonction-constante) 53 | * [Foncteur constant](#foncteur-constant) 54 | * [Monade constante](#monade-constante) 55 | * [Foncteur](#foncteur) 56 | * [Foncteur pointé](#foncteur-pointé) 57 | * [Relèvement](#relèvement) 58 | * [Transparence référentielle](#transparence-référentielle) 59 | * [Raisonnement équationnel](#raisonnement-équationnel) 60 | * [Lambda](#lambda) 61 | * [Lambda-calcul](#lambda-calcul) 62 | * [Combinateur fonctionnel](#combinateur-fonctionnel) 63 | * [Évaluation paresseuse](#évaluation-paresseuse) 64 | * [Monoïde](#monoïde) 65 | * [Monade](#monade) 66 | * [Comonade](#comonade) 67 | * [Composition de Kleisli](#composition-de-kleisli) 68 | * [Foncteur applicatif](#foncteur-applicatif) 69 | * [Morphisme](#morphisme) 70 | * [Homomorphisme](#homomorphisme) 71 | * [Endomorphisme](#endomorphisme) 72 | * [Isomorphisme](#isomorphisme) 73 | * [Catamorphisme](#catamorphisme) 74 | * [Anamorphisme](#anamorphisme) 75 | * [Hylomorphisme](#hylomorphisme) 76 | * [Paramorphisme](#paramorphisme) 77 | * [Apomorphisme](#apomorphisme) 78 | * [Setoïde](#setoïde) 79 | * [Demi-groupe](#demi-groupe) 80 | * [Foldable](#foldable) 81 | * [Lentille](#lentille) 82 | * [Signatures de type](#signatures-de-type) 83 | * [Type algébrique de données](#type-algébrique-de-données) 84 | * [Types somme](#types-somme) 85 | * [Types produit](#types-produit) 86 | * [Option](#option) 87 | * [Fonction](#fonction) 88 | * [Fonction partielle](#fonction-partielle) 89 | * [Manipuler des fonctions partielles](#manipuler-des-fonctions-partielles) 90 | * [Fonction totale](#fonction-totale) 91 | * [Bibliothèques de programmation fonctionnelle en JavaScript](#bibliothèques-de-programmation-fonctionnelle-en-javascript) 92 | 93 | 94 | 95 | ## Arité 96 | 97 | L'arité (_arity_ en anglais) est le nombre d’arguments déclaré par une fonction. On utilise aussi des mots comme unaire, 98 | binaire ou ternaire pour désigner le nombre d'arguments. 99 | 100 | ```js 101 | const somme = (a, b) => a + b // l’arité est 2 (binaire) 102 | const inc = a => a + 1 // l’arité est 1 (unaire) 103 | const zero = () => 0 // l’arité est 0 (nullaires) 104 | ``` 105 | 106 | __Pour aller plus loin__ 107 | 108 | * [Arité](https://fr.wikipedia.org/wiki/Arit%C3%A9) sur Wikipédia 109 | 110 | ## Fonction d’ordre supérieur 111 | 112 | Une fonction d’ordre supérieur (_higher-order functions_ ou _HOF_ en anglais) est une fonction prenant une autre 113 | fonction en argument et/ou renvoyant une autre fonction. 114 | 115 | ```js 116 | const filtre = (predicat, elements) => elements.filter(predicat) 117 | const est = (type) => (element) => Object(element) instanceof type 118 | 119 | filtre(est(Number), [0, '1', 2, null]) // [0, 2] 120 | ``` 121 | 122 | __Pour aller plus loin__ 123 | 124 | * [Fonction d’ordre supérieur](https://fr.wikipedia.org/wiki/Fonction_d%27ordre_sup%C3%A9rieur) sur Wikipédia 125 | 126 | ## Fermeture 127 | 128 | Une fermeture (_closure_ en anglais) est une fonction accompagnée de l’ensemble des variables locales qu’elle a 129 | capturées au moment où elle a été définie. Ces variables restent alors accessibles même après que le programme soit 130 | sorti du bloc où elles ont été définies. 131 | 132 | ```js 133 | const ajouterA = x => y => x + y 134 | const ajouterACinq = ajouterA(5) 135 | ajouterACinq(3) // => 8 136 | ``` 137 | 138 | Dans l’exemple, `x` a été capturée par la fermeture `ajouterACinq` avec la valeur `5`. `ajouterACinq` peut alors être 139 | appelée avec `y` pour retourner la somme. 140 | 141 | __Pour aller plus loin__ 142 | 143 | * [Closures (Fermetures)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Closures) sur MDN 144 | * [Lambda Vs Closure (en)](http://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda) 145 | * [JavaScript Closures highly voted discussion (en)](http://stackoverflow.com/questions/111102/how-do-javascript-closures-work) 146 | 147 | ## Application partielle 148 | 149 | L'application partielle d’une fonction (_partial application_ en anglais) est le processus visant à créer une nouvelle 150 | fonction en préremplissant certains des arguments de la fonction originale. 151 | 152 | ```js 153 | // permet de créer des fonctions appliquées partiellement 154 | // prend en paramètre une fonction et des arguments 155 | const appliquerPartiellement = (f, ...args) => 156 | // retourne une function qui prend en paramètre le reste des arguments 157 | (...resteDesArgs) => 158 | // et appelle la fonction originale avec tous les arguments 159 | f(...args, ...resteDesArgs) 160 | 161 | // une fonction à appliquer partiellement 162 | const ajouter3 = (a, b, c) => a + b + c 163 | 164 | // Applique partiellement `2` et `3` à `ajouter3` produit une fonction à un argument 165 | const cinqPlus = appliquerPartiellement(ajouter3, 2, 3) // (c) => 2 + 3 + c 166 | 167 | cinqPlus(4) // 9 168 | ``` 169 | 170 | Il est aussi possible d’utiliser `Function.prototype.bind` pour appliquer partiellement une fonction en JavaScript : 171 | 172 | ```js 173 | const cinqPlus = ajouter3.bind(null, 2, 3) // (c) => 2 + 3 + c 174 | ``` 175 | 176 | L’application partielle permet de créer des fonctions plus simples à partir de fonctions plus complexes en y intégrant 177 | les données au fur et à mesure où elles deviennent disponibles. Les fonctions [curryfiées](#curryfication) sont 178 | automatiquement partiellement appliquées. 179 | 180 | __Pour aller plus loin__ 181 | 182 | * [Partial application (en)](https://en.wikipedia.org/wiki/Partial_application) sur Wikipédia 183 | 184 | ## Curryfication 185 | 186 | La curryfication (_currying_ en anglais) est le processus de conversion d’une fonction qui prend plusieurs arguments en 187 | une fonction qui les prend un à la fois. 188 | 189 | Chaque fois que la fonction est appelée, elle n’accepte qu’un seul argument et retourne une fonction qui prend un 190 | argument jusqu’à ce que tous les arguments soient passés. 191 | 192 | ```js 193 | const somme = (a, b) => a + b 194 | 195 | const sommeCurryfiee = (a) => (b) => a + b 196 | 197 | sommeCurryfiee(40)(2) // 42 198 | 199 | const ajouterDeux = sommeCurryfiee(2) // (b) => 2 + b 200 | 201 | ajouterDeux(10) // 12 202 | ``` 203 | 204 | __Pour aller plus loin__ 205 | 206 | * [Curryfication](https://fr.wikipedia.org/wiki/Curryfication) sur Wikipédia 207 | 208 | ## Auto-curryfication 209 | 210 | L'auto-curryfication (_auto currying_ en anglais) est la transformation d’une fonction qui prend plusieurs arguments en 211 | une fonction qui, si on lui donne moins que son nombre correct d’arguments, renvoie une fonction qui prend le reste des 212 | arguments. Lorsque la fonction est appelée avec le nombre correct d’arguments, elle est ensuite évaluée. 213 | 214 | [Lodash](https://lodash.com/) et [Ramda](https://ramdajs.com/) ont une fonction `curry` qui marche comme suit : 215 | 216 | ```js 217 | const somme = (x, y) => x + y 218 | 219 | const sommeCurryfiee = _.curry(somme) 220 | sommeCurryfiee(1, 2) // 3 221 | sommeCurryfiee(1) // (y) => 1 + y 222 | sommeCurryfiee(1)(2) // 3 223 | ``` 224 | 225 | __Pour aller plus loin__ 226 | 227 | * [Favoring Curry (en)](http://fr.umio.us/favoring-curry/) 228 | * [Hey Underscore, You’re Doing It Wrong! (en)](https://www.youtube.com/watch?v=m3svKOdZijA) 229 | 230 | ## Composition de fonctions 231 | 232 | La composition de fonctions (_function composition_ en anglais) est l’acte d'assembler deux fonctions ensemble pour en 233 | former une troisième, où la sortie d’une fonction est l’entrée de l’autre. C’est l’une des idées les plus importantes de 234 | la programmation fonctionnelle. 235 | 236 | ```js 237 | const composer = (f, g) => (a) => f(g(a)) // Définition 238 | const arondirEtTransformerEnChaine = composer((val) => val.toString(), Math.floor) // Utilisation 239 | arondirEtTransformerEnChaine(121.212121) // '121' 240 | ``` 241 | 242 | __Pour aller plus loin__ 243 | 244 | * [Composition de fonctions](https://fr.wikipedia.org/wiki/Composition_de_fonctions) sur Wikipédia 245 | 246 | ## Continuation 247 | 248 | À tout moment d’un programme, la partie du code qui n’a pas encore été exécutée est appelée _continuation_. 249 | 250 | ```js 251 | const afficher = (nombre) => console.log(`Nombre ${nombre}`) 252 | 253 | const ajouterUnEtContinuer = (nombre, cc) => { 254 | const resultat = nombre + 1 255 | cc(resultat) 256 | } 257 | 258 | ajouterUnEtContinuer(2, afficher) // 'Nombre 3' 259 | ``` 260 | 261 | Les continuations sont souvent observées en programmation asynchrone lorsque le programme doit attendre de recevoir des 262 | données avant de pouvoir continuer à s’exécuter. La réponse est généralement transmise au reste du programme, qui est la 263 | continuation, une fois qu’elle a été reçue. 264 | 265 | ```js 266 | const poursuivreLeProgrammeAvec = (donnees) => { 267 | // Poursuit le programme avec les données en paramètre 268 | } 269 | 270 | lireFichierAsync('chemin/vers/fichier', (err, reponse) => { 271 | if (err) { 272 | // prend en charge l’erreur 273 | return 274 | } 275 | poursuivreLeProgrammeAvec(reponse) 276 | }) 277 | ``` 278 | 279 | __Pour aller plus loin__ 280 | 281 | * [Continuation](https://fr.wikipedia.org/wiki/Continuation_(informatique)) sur Wikipédia 282 | 283 | ## Fonction pure 284 | 285 | Une fonction pure (_pure function_ en anglais) est une fonction : 286 | 287 | * dont la valeur de retour n’est déterminée que par ses arguments, 288 | * qui ne produit pas d’effets secondaires. 289 | 290 | Une fonction pure doit toujours renvoyer le même résultat lorsqu’elle reçoit les mêmes arguments. 291 | 292 | ```js 293 | const saluer = (nom) => `Salut, ${nom}` 294 | 295 | saluer('Brianne') // renvoie toujour 'Salut, Brianne' 296 | ``` 297 | 298 | La fonction `saluer` suivante n'est pas pure, son résultat dépend de données stockées en dehors de la fonction : 299 | 300 | ```js 301 | window.name = 'Brianne' 302 | 303 | const saluer = () => `Salut, ${window.name}` 304 | 305 | saluer() // "Salut, Brianne" 306 | ``` 307 | 308 | La fonction `saluer` suivante n'est pas pure, l’état en dehors de la fonction est modifié. 309 | 310 | ```js 311 | let salutation 312 | 313 | const saluer = (nom) => { 314 | salutation = `Salut, ${nom}` 315 | } 316 | 317 | saluer('Brianne') 318 | salutation // "Salut, Brianne" 319 | ``` 320 | 321 | __Pour aller plus loin__ 322 | 323 | * [Fonction pure](https://fr.wikipedia.org/wiki/Fonction_pure) sur Wikipédia 324 | 325 | ## Effets de bord 326 | 327 | Une fonction ou une expression est dite à effet de bord (_side effects_ en anglais) si, en plus de renvoyer une valeur, 328 | elle interagit (lit ou écrit) avec son environnement externe et que ce dernier est mutable. 329 | 330 | ```js 331 | const differentAChaqueFois = new Date() // la date change continuellement 332 | 333 | console.log('Les entrées-sorties sont un effet de bord !') 334 | ``` 335 | 336 | __Pour aller plus loin__ 337 | 338 | * [Effet de bord](https://fr.wikipedia.org/wiki/Effet_de_bord_(informatique)) sur Wikipédia 339 | 340 | ## Idempotence 341 | 342 | Une fonction est idempotente (_idempotent_ en anglais) si, quand on l’applique à nouveau à son résultat, elle ne produit 343 | pas un résultat différent. 344 | 345 | ```js 346 | Math.abs(Math.abs(10)) // le résultat sera toujours 10 quelque soit le nombre d'appel à Math.abs 347 | 348 | sort(sort(sort([2, 1]))) // une fois la liste triée, le résultat ne change plus 349 | ``` 350 | 351 | __Pour aller plus loin__ 352 | 353 | * [Idempotence](https://fr.wikipedia.org/wiki/Idempotence) sur Wikipédia 354 | 355 | ## Programmation tacite 356 | 357 | La programmation tacite (_tacit programming_ ou _point-free style_ en anglais) est une manière d’écrire une fonction en 358 | n’identifiant pas explicitement les arguments utilisés. Ce style de programmation requiert généralement les notions 359 | de [curryfication](#curryfication) ou d'autres [fonctions d’ordre supérieur](#fonction-dordre-supérieur). 360 | 361 | ```js 362 | // Étant donné 363 | const map = (fn) => (liste) => liste.map(fn) 364 | const ajouter = (a) => (b) => a + b 365 | 366 | // Alors 367 | 368 | // Sans la programmation tacite - `nombres` est un argument explicite 369 | const incrementerTout = (nombres) => map(ajouter(1))(nombres) 370 | 371 | // Avec la programmation tacite - La liste est un argument implicite 372 | const incrementerTout2 = map(ajouter(1)) 373 | ``` 374 | 375 | Les définitions de fonctions _point-free_ ressemblent à des affectations normales sans `function` ou `=>`. Il convient 376 | de mentionner que les fonctions _point-free_ ne sont pas nécessairement meilleures que leurs homologues 377 | non-_point-free_, car elles peuvent être plus difficiles à comprendre lorsqu’elles sont complexes. 378 | 379 | __Pour aller plus loin__ 380 | 381 | * [Tacit programming](https://en.wikipedia.org/wiki/Tacit_programming) sur Wikipédia 382 | 383 | ## Prédicat 384 | 385 | Un prédicat (_predicate_ en anglais) est une fonction qui retourne vrai ou faux pour une valeur donnée. Une utilisation 386 | courante d’un prédicat est la fonction utilisée pour filtrer un tableau. 387 | 388 | ```js 389 | const predicat = (a) => a > 2 390 | 391 | [1, 2, 3, 4].filter(predicat) // [3, 4] 392 | ``` 393 | 394 | ## Contrats 395 | 396 | Un contrat (_contract_ en anglais) spécifie les obligations et les garanties de comportement qu’une fonction ou une 397 | expression doit respecter lors de son exécution. C’est un ensemble de règles qui s’appliquent aux entrées et sorties 398 | d’une fonction ou d’une expression. Des erreurs sont levées quand le contrat n’est pas respecté. 399 | 400 | ```js 401 | // Definie le contrat : int -> boolean 402 | const contrat = (entree) => { 403 | if (typeof entree === 'number') return true 404 | throw new Error('Contrat non respecté : attendu int -> boolean') 405 | } 406 | 407 | const ajouterUn = (nombre) => contrat(nombre) && nombre + 1 408 | 409 | ajouterUn(2) // 3 410 | ajouterUn('une chaine') // Contrat non respecté : attendu int -> boolean 411 | ``` 412 | 413 | ## Catégorie 414 | 415 | Une catégorie (_category_ en anglais), dans la théorie des catégories, est un ensemble composé d’objets et des 416 | morphismes entre eux. En programmation, les types représentent généralement les objets et les fonctions représentent 417 | les morphismes. 418 | 419 | Pour qu’une catégorie soit valide, trois règles doivent être respectées : 420 | 421 | 1. Un morphisme d’identité, qui mappe un objet à lui-même, doit exister. 422 | Si `a` est un objet d’une catégorie, 423 | alors il doit y avoir une fonction de `a -> a`. 424 | 2. Les morphismes doivent pouvoir être composés. 425 | Si `a`, `b`, and `c` sont des objets d’une catégorie, 426 | `f` est un morphisme de `a -> b` et `g` est un morphisme de `b -> c`, 427 | alors `g(f(x))` doit être équivalent à `(g • f)(x)`. 428 | 3. La composition doit être associative. 429 | `f • (g • h)` est équivalent à `(f • g) • h` 430 | 431 | Puisque ces règles régissent la composition à un niveau très abstrait, la théorie des catégories est excellente pour 432 | découvrir de nouvelles façons de composer des choses. 433 | 434 | À titre d’exemple, nous pouvons définir une catégorie Max en tant que classe : 435 | 436 | ```js 437 | 438 | class Max { 439 | constructor (a) { 440 | this.a = a 441 | } 442 | 443 | id () { 444 | return this 445 | } 446 | 447 | compose (b) { 448 | return this.a > b.a ? this : b 449 | } 450 | 451 | toString () { 452 | return `Max(${this.a})` 453 | } 454 | } 455 | 456 | new Max(2).compose(new Max(3)).compose(new Max(5)).id().id() // => Max(5) 457 | ``` 458 | 459 | __Pour aller plus loin__ 460 | 461 | * [Théorie des catégories](https://fr.wikipedia.org/wiki/Th%C3%A9orie_des_cat%C3%A9gories) sur Wikipédia 462 | * [Category Theory for Programmers (en)](https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/) 463 | 464 | ## Valeur 465 | 466 | Une valeur (_value_ en anglais) est tout ce qui peut être assigné à une variable. 467 | 468 | ```js 469 | 5 470 | Object.freeze({ nom: 'John', age: 30 }) // La fonction `freeze` renforce l’immutabilité. 471 | ;(a) => a 472 | ;[1] 473 | undefined 474 | ``` 475 | 476 | ## Constante 477 | 478 | Une constante (_constant_ en anglais) est une variable qui ne peut pas être réaffectée une fois définie. 479 | 480 | ```js 481 | const cinq = 5 482 | const john = Object.freeze({ name: 'John', age: 30 }) // La fonction `freeze` renforce l’immutabilité. 483 | ``` 484 | 485 | Les constantes sont [référentiellement transparentes](#transparence-référentielle). Cela veut dire qu’une constante peut 486 | être remplacée par la valeur qu’elle représente sans que le résultat du programme soit modifié. 487 | 488 | Avec les deux constantes ci-dessus, l’expression suivante renverra toujours `true`. 489 | 490 | ```js 491 | john.age + cinq === ({ name: 'John', age: 30 }).age + 5 492 | ``` 493 | 494 | __Pour aller plus loin__ 495 | 496 | * [Constante](https://fr.wikipedia.org/wiki/Constante_(programmation_informatique)) sur Wikipédia 497 | 498 | ### Fonction constante 499 | 500 | Une fonction constante (_constant function_ en anglais) est une fonction [curryfiée](#curryfication) qui ignore son 501 | deuxième argument : 502 | 503 | ```js 504 | const constante = a => () => a 505 | 506 | ;[1, 2].map(constante(0)) // => [0, 0] 507 | ;[2, 3].map(constante(0)) // => [0, 0] 508 | ``` 509 | 510 | ### Foncteur constant 511 | 512 | Un foncteur constant (_constant functor_ en anglais) est un object dont la fonction `map` ne transforme pas le contenu. 513 | Voir [Foncteur](#foncteur). 514 | 515 | ```js 516 | Constante(1).map(n => n + 1) // => Constante(1) 517 | ``` 518 | 519 | ### Monade constante 520 | 521 | Une monade constante (_constant monad_ en anglais) est un object dont la fonction `chain` ne transforme pas le contenu. 522 | Voir [Monade](#monade). 523 | 524 | ```js 525 | Constante(1).chain(n => Constante(n + 1)) // => Constante(1) 526 | ``` 527 | 528 | ## Foncteur 529 | 530 | Un foncteur (_functor_ en anglais) est un objet qui implémente une fonction `map` qui prend en paramètre une fonction 531 | qui sera exécutée sur son contenu. Un foncteur doit respecter deux règles : 532 | 533 | __Préserver l’identité__ 534 | 535 | ```js 536 | objet.map(x => x) // = objet 537 | ``` 538 | 539 | __Être composable__ 540 | 541 | ```js 542 | // `f`, `g` sont des fonctions composables arbitraires 543 | objet.map(x => g(f(x))) // = objet.map(f).map(g) 544 | ``` 545 | 546 | L’implémentation de référence d’[Option](#option) est un foncteur, en effet : 547 | 548 | ```js 549 | // préserve l’identité 550 | Some(1).map(x => x) // = Some(1) 551 | 552 | // est composable 553 | const f = x => x + 1 554 | const g = x => x * 2 555 | Some(1).map(x => g(f(x))) // = Some(4) 556 | Some(1).map(f).map(g) // = Some(4) 557 | ``` 558 | 559 | __Pour aller plus loin__ 560 | 561 | * [Foncteur](https://fr.wikipedia.org/wiki/Foncteur) sur Wikipédia 562 | 563 | ## Foncteur pointé 564 | 565 | Un foncteur pointé (_pointed functor_ en anglais) est un objet avec une fonction `of` qui stocke _n’importe_ quelle 566 | valeur. 567 | 568 | Quand ES2015 a ajouté `Array.of`, `Array` est devenu un foncteur pointé. 569 | 570 | ```js 571 | Array.of(1) // [1] 572 | ``` 573 | 574 | ## Relèvement 575 | 576 | Le relèvement (_lifting_ en anglais) est l’action de prendre une valeur et de la mettre dans un objet tel qu’un 577 | [foncteur](#foncteur-pointé). Si vous relevez une fonction dans un [foncteur applicatif](#foncteur-applicatif), vous 578 | pouvez l’appliquer aux valeurs qui se trouvent également dans ce foncteur. 579 | 580 | Certaines implémentations ont une fonction appelée `lift` ou `liftA2` pour faciliter l’exécution de fonctions sur des 581 | foncteurs. 582 | 583 | ```js 584 | const liftA2 = (f) => (a, b) => a.map(f).ap(b) // note: c’est bien `ap` et non `map`. 585 | 586 | const mult = a => b => a * b 587 | 588 | const multRelevé = liftA2(mult) // cette nouvelle fonction s’applique à des foncteurs comme Array 589 | 590 | multRelevé([1, 2], [3]) // [3, 6] 591 | liftA2(a => b => a + b)([1, 2], [30, 40]) // [31, 41, 32, 42] 592 | ``` 593 | 594 | Relever une fonction à un argument et l’appliquer fait la même chose que `map`. 595 | 596 | ```js 597 | const incrementer = (x) => x + 1 598 | 599 | lift(incrementer)([2]) // [3] 600 | ;[2].map(incrementer) // [3] 601 | ``` 602 | 603 | Relever de simples valeurs peut consister à créer uniquement l’objet. 604 | 605 | ```js 606 | Array.of(1) // => [1] 607 | ``` 608 | 609 | ## Transparence référentielle 610 | 611 | Une expression qui peut être remplacée par sa valeur sans changer le comportement du programme est dite 612 | référentiellement transparente (_referentially transparent_ en anglais). 613 | 614 | Étant donné la fonction `saluer` : 615 | 616 | ```js 617 | const saluer = () => 'Bonjour le monde !' 618 | ``` 619 | 620 | Toute invocation de `saluer()` peut être remplacée par `Bonjour le monde !`, donc `saluer` est référentiellement 621 | transparente. Cela ne serait pas le cas si `saluer` dépendait d’un état externe comme de la configuration ou un appel à 622 | une base de données. Voir aussi [fonction pure](#fonction-pure), [raisonnement équationnel](#raisonnement-équationnel). 623 | 624 | __Pour aller plus loin__ 625 | 626 | * [Transparence référentielle](https://fr.wikipedia.org/wiki/Transparence_r%C3%A9f%C3%A9rentielle) sur Wikipédia 627 | 628 | ## Raisonnement équationnel 629 | 630 | Lorsqu’une application est composée d’expressions et dépourvue d’effets secondaires, des affirmations peuvent être 631 | déduites de ses parties. Vous pouvez également être sûr des détails de votre programme sans avoir à parcourir toutes les 632 | fonctions. 633 | 634 | ```js 635 | const grainesDansChiens = compose(pouletsDansChiens, grainesDansPoulets) 636 | const grainesDansChats = compose(chiensDansChats, grainesDansChiens) 637 | ``` 638 | 639 | Dans l’exemple ci-dessus, si vous savez que `pouletsDansChiens` et `grainesDansPoulets` sont [pures](#fonction-pure), 640 | alors vous savez que la composition est également pure. Les choses peuvent être poussées un peu plus loin lorsque l’on 641 | en sait plus sur les fonctions (associativité, commutativité, idempotence, etc...). 642 | 643 | ## Lambda 644 | 645 | Une fonction anonyme qui peut être traitée comme une valeur. 646 | 647 | ```js 648 | ;(function (a) { 649 | return a + 1 650 | }) 651 | 652 | ;(a) => a + 1 653 | ``` 654 | 655 | Les lambdas sont souvent utilisées en tant qu’arguments de fonctions d’ordre supérieur. 656 | 657 | ```js 658 | ;[1, 2].map((a) => a + 1) // [2, 3] 659 | ``` 660 | 661 | Une lambda peut être affectée à une variable : 662 | 663 | ```js 664 | const ajouter1 = (a) => a + 1 665 | ``` 666 | 667 | ## Lambda-calcul 668 | 669 | Une branche des mathématiques qui utilise les fonctions pour créer un 670 | [modèle universel de calcul](https://fr.wikipedia.org/wiki/Lambda-calcul). 671 | 672 | ## Combinateur fonctionnel 673 | 674 | Un combinateur fonctionnel (_functional combinator_ en anglais) est une 675 | [fonction d’ordre supérieur](##fonction-dordre-supérieur), généralement [curryfiée](#curryfication), qui renvoie une 676 | nouvelle fonction modifiée d’une manière ou d’une autre. 677 | 678 | Les combinateurs fonctionnels sont souvent utilisés en [programmation tacite](#programmation-tacite) pour écrire des 679 | programmes particulièrement concis. 680 | 681 | ```js 682 | // Le combinateur "C" prend une fonction curryfiée à deux arguments et 683 | // en renvoie une nouvelle qui appelle la fonction d’origine avec les arguments inversés. 684 | const C = (f) => (a) => (b) => f(b)(a) 685 | 686 | const diviser = (a) => (b) => a / b 687 | 688 | const diviserPar = C(diviser) 689 | 690 | const diviserPar10 = diviserPar(10) 691 | 692 | diviserPar10(30) // => 3 693 | ``` 694 | 695 | __Pour aller plus loin__ 696 | 697 | * [Logique combinatoire](https://fr.wikipedia.org/wiki/Logique_combinatoire) sur Wikipédia 698 | * [liste des combinateurs fonctionnels en JavaScript (en)](https://gist.github.com/Avaq/1f0636ec5c8d6aed2e45) 699 | 700 | ## Évaluation paresseuse 701 | 702 | L’évaluation paresseuse (_lazy evaluation_ en anglais) est un mécanisme qui retarde l’évaluation d’une expression 703 | jusqu’à ce que sa valeur soit nécessaire. Dans les langages fonctionnels, cela permet des structures telles que les 704 | listes infinies, qui ne seraient normalement pas disponibles dans un langage impératif où l’enchaînement des commandes 705 | est important. 706 | 707 | ```js 708 | const rand = function * () { 709 | while (1 < 2) { 710 | yield Math.random() 711 | } 712 | } 713 | 714 | const randIter = rand() 715 | randIter.next() // Chaque exécution donne une valeur aléatoire, l’expression est évaluée au besoin. 716 | ``` 717 | 718 | __Pour aller plus loin__ 719 | 720 | * [Évaluation paresseuse](https://fr.wikipedia.org/wiki/%C3%89valuation_paresseuse) sur Wikipédia 721 | 722 | ## Monoïde 723 | 724 | Un monoïde (_monoid_ en anglais) est un objet avec une fonction qui « combine » cet objet avec un autre du même type 725 | ([demi-groupe](#demi-groupe)) et qui a de plus une valeur neutre. 726 | 727 | Un exemple de monoïde simple est l’addition de nombres : 728 | 729 | ```js 730 | 1 + 1 // 2 731 | ``` 732 | 733 | Dans ce cas, le nombre est l’objet et `+` est la fonction. 734 | 735 | Lorsqu’une valeur est combinée avec la valeur neutre, le résultat doit être la valeur d’origine. La fonction doit 736 | également être commutative dans ce cas. La valeur neutre pour l’addition est `0` : 737 | 738 | ```js 739 | 1 + 0 // 1 740 | 0 + 1 // 1 741 | 1 + 0 === 0 + 1 742 | ``` 743 | 744 | Il est également nécessaire que le regroupement des opérations n’influe pas sur le résultat (associativité) : 745 | 746 | ```js 747 | 1 + (2 + 3) === (1 + 2) + 3 // true 748 | ``` 749 | 750 | La concaténation de tableaux forme également un monoïde dont la valeur neutre est un tableau vide (`[]`) : 751 | 752 | ```js 753 | ;[1, 2].concat([3, 4]) // [1, 2, 3, 4] 754 | ;[1, 2].concat([]) // [1, 2] 755 | ``` 756 | 757 | À titre de contre-exemple, la soustraction ne forme pas de monoïde car il n’existe pas de valeur neutre commutative : 758 | 759 | ```js 760 | 0 - 4 === 4 - 0 // false 761 | ``` 762 | 763 | __Pour aller plus loin__ 764 | 765 | * [Monoïde](https://fr.wikipedia.org/wiki/Mono%C3%AFde) sur Wikipédia 766 | 767 | ## Monade 768 | 769 | Une monade (_monad_ en anglais) est un objet avec les fonctions [`of`](#foncteur-pointé) et `chain`. La fonction `chain` 770 | est comme [`map`](#foncteur) sauf qu’elle désimbrique les objets imbriqués résultants. 771 | 772 | ```js 773 | // Implémentation 774 | Array.prototype.chain = function (f) { 775 | return this.reduce((acc, it) => acc.concat(f(it)), []) 776 | } 777 | 778 | // Utilisation 779 | Array.of('chat,chien', 'poisson,oiseau').chain((a) => a.split(',')) // ['chat', 'chien', 'poisson', 'oiseau'] 780 | 781 | // Contraste avec map 782 | Array.of('chat,chien', 'poisson,oiseau').map((a) => a.split(',')) // [['chat', 'chien'], ['poisson', 'oiseau']] 783 | ``` 784 | 785 | `of` est également connu sous le nom de `return` dans d’autres langages fonctionnels. Et `chain` est connu sous le nom 786 | de `flatmap` ou `bind`. 787 | 788 | __Pour aller plus loin__ 789 | 790 | * [Monade](https://fr.wikipedia.org/wiki/Monade_(informatique)) sur Wikipédia 791 | 792 | ## Comonade 793 | 794 | Une comonade (_comonad_ en anglais) est objet qui a les fonctions `extract` et `extend`. 795 | 796 | ```js 797 | const CoIdentité = (v) => ({ 798 | val: v, 799 | extract () { 800 | return this.val 801 | }, 802 | extend (f) { 803 | return CoIdentité(f(this)) 804 | } 805 | }) 806 | ``` 807 | 808 | `extract` extrait une valeur d’un foncteur : 809 | 810 | ```js 811 | CoIdentité(1).extract() // 1 812 | ``` 813 | 814 | `extend` exécute une fonction sur la comonade. La fonction doit renvoyer le même type que la comonade : 815 | 816 | ```js 817 | CoIdentité(1).extend((co) => co.extract() + 1) // CoIdentité(2) 818 | ``` 819 | 820 | __Pour aller plus loin__ 821 | 822 | * [Comonads (en)](https://en.wikipedia.org/wiki/Monad_(category_theory)#Comonads) sur Wikipédia 823 | 824 | ## Composition de Kleisli 825 | 826 | Une composition de Kleisli (_Kleisli composition_ en anglais) est une opération pour composer deux fonctions qui 827 | retournent des [monades](#monade) (flèches de Kleisli) quand elles ont des types compatibles. En Haskell, il s’agit de 828 | l’opérateur `>=>`. 829 | 830 | En JavaScript, en utilisant [les options](#option) : 831 | 832 | ```js 833 | // safeParseNum :: String -> Option Number 834 | const safeParseNum = (b) => { 835 | const n = parseNumber(b) 836 | return isNaN(n) ? None() : Some(n) 837 | } 838 | 839 | // validatePositive :: Number -> Option Number 840 | const validatePositive = (a) => a > 0 ? Some(a) : None() 841 | 842 | // kleisliCompose :: Monad M => ((b -> M c), (a -> M b)) -> a -> M c 843 | const kleisliCompose = (g, f) => (x) => f(x).chain(g) 844 | 845 | // parseAndValidate :: String -> Option Number 846 | const parseAndValidate = kleisliCompose(validatePositive, safeParseNum) 847 | 848 | parseAndValidate('1') // => Some(1) 849 | parseAndValidate('asdf') // => None 850 | parseAndValidate('999') // => Some(999) 851 | ``` 852 | 853 | Ceci fonctionne car : 854 | 855 | * [Option](#option) est une [monade](#monade) 856 | * `validatePositive` et `safeParseNum` renvoient le même type de monade (Option). 857 | * Le type d’argument de `validatePositive` correspond au retour non encapsulé de `safeParseNum`. 858 | 859 | __Pour aller plus loin__ 860 | 861 | * [Catégorie de Kleisli](https://fr.wikipedia.org/wiki/Cat%C3%A9gorie_de_Kleisli) sur Wikipédia 862 | 863 | ## Foncteur applicatif 864 | 865 | Un foncteur applicatif (_applicative functor_ en anglais) est un objet avec une fonction `ap`. `ap` applique une 866 | fonction de l’objet à une valeur d’un autre objet du même type. 867 | 868 | ```js 869 | // Implémentation 870 | Array.prototype.ap = function (elements) { 871 | return this.reduce((acc, f) => acc.concat(elements.map(f)), []) 872 | } 873 | 874 | // Exemple d’utilisation 875 | ;[(a) => a + 1].ap([1]) // [2] 876 | ``` 877 | 878 | C’est très utile quand vous avez deux objets et que vous souhaitez appliquer une fonction binaire à leurs contenus. 879 | 880 | ```js 881 | // Tableaux que vous souhaitez fusionner 882 | const arg1 = [1, 3] 883 | const arg2 = [4, 5] 884 | 885 | // fonction de fusion - doit être curryfiée pour que cela fonctionne 886 | const ajouter = (x) => (y) => x + y 887 | 888 | const ajoutsPartiels = [ajouter].ap(arg1) // [(y) => 1 + y, (y) => 3 + y] 889 | ``` 890 | 891 | Cela vous donne un tableau de fonctions sur lequel vous pouvez appeler `ap` pour obtenir le résultat final : 892 | 893 | ```js 894 | ajoutsPartiels.ap(arg2) // [5, 6, 7, 8] 895 | ``` 896 | 897 | __Pour aller plus loin__ 898 | 899 | * [Applicative functor](https://en.wikipedia.org/wiki/Applicative_functor) sur Wikipédia 900 | 901 | ## Morphisme 902 | 903 | Un morphisme (_morphism_ en anglais) est une relation entre des objets au sein d’une [catégorie](#catégorie). En 904 | programmation fonctionnelle, toutes les fonctions sont des morphismes. 905 | 906 | __Pour aller plus loin__ 907 | 908 | * [Morphisme](https://fr.wikipedia.org/wiki/Morphisme) sur Wikipédia 909 | 910 | ### Homomorphisme 911 | 912 | Un homomorphisme (_homomorphism_ en anglais) et une fonction où l’entrée et la sortie partagent une même propriété 913 | structurelle. Par exemple, dans un homomorphisme [monoïde](#monoïde), l’entrée et la sortie sont des monoïdes même si 914 | leurs types sont différents. 915 | 916 | ```js 917 | // convertir :: [number] -> string 918 | const convertir = (a) => a.join(', ') 919 | ``` 920 | 921 | `convertir` est un homomorphisme car : 922 | 923 | * array est un monoïde - il possède une opération `concat` et une valeur neutre (`[]`), 924 | * string est un monoïde - il possède une opération `concat` et une valeur neutre (`''`). 925 | 926 | Ainsi, un homomorphisme se rapporte à la propriété qui vous intéresse dans l’entrée et la sortie d’une fonction de 927 | transformation. 928 | 929 | Les [endomorphismes](#endomorphisme) et les [isomorphismes](#isomorphisme) sont des exemples d’homomorphisme. 930 | 931 | __Pour aller plus loin__ 932 | 933 | * [Homomorphism | Learning Functional Programming in Go (en)](https://subscription.packtpub.com/book/application-development/9781787281394/11/ch11lvl1sec90/homomorphism#:~:text=A%20homomorphism%20is%20a%20correspondence,pointing%20to%20it%20from%20A.) 934 | 935 | ### Endomorphisme 936 | 937 | Un endomorphisme (_endomorphism_ en anglais) est fonction où le type de l’entrée est identique à celui de sortie. 938 | 939 | ```js 940 | // majuscule :: String -> String 941 | const enMajuscule = (str) => str.toUpperCase() 942 | 943 | // decrementer :: Number -> Number 944 | const decrementer = (x) => x - 1 945 | ``` 946 | 947 | __Pour aller plus loin__ 948 | 949 | * [Endomorphisme](https://fr.wikipedia.org/wiki/Endomorphisme) sur Wikipédia 950 | 951 | ### Isomorphisme 952 | 953 | Un isomorphisme (_isomorphism_ en anglais) est un morphisme pour lequel il existe un morphisme inverse. Ce type de 954 | morphismes préserve la structure des objets. 955 | 956 | Par exemple, les coordonnées 2D peuvent être stockées sous forme de tableau (`[2,3]`) ou d’objet (`{x: 2, y: 3}`). 957 | 958 | ```js 959 | // Fonctions de conversions 960 | const paireVersCoords = (paire) => ({ x: paire[0], y: paire[1] }) 961 | const coordsVersPaire = (coords) => [coords.x, coords.y] 962 | 963 | coordsVersPaire(paireVersCoords([1, 2])) // [1, 2] 964 | paireVersCoords(coordsVersPaire({ x: 1, y: 2 })) // {x: 1, y: 2} 965 | ``` 966 | 967 | Les isomorphismes sont aussi des [homomorphismes](#homomorphisme) puisque les types d’entrée et de sortie sont 968 | réversibles. 969 | 970 | __Pour aller plus loin__ 971 | 972 | * [Isomorphisme](https://fr.wikipedia.org/wiki/Isomorphisme) sur Wikipédia 973 | 974 | ### Catamorphisme 975 | 976 | Un catamorphisme (_catamorphism_ en anglais) est ue fonction qui déconstruit une structure en une seule valeur. 977 | `reduceRight` est un exemple de catamorphisme pour les structures de type tableau. 978 | 979 | ```js 980 | // somme est un catamorphisme de [Number] -> Number 981 | const somme = nombres => nombres.reduceRight((accumulateur, nombre) => accumulateur + nombre, 0) 982 | 983 | somme([1, 2, 3, 4, 5]) // 15 984 | ``` 985 | 986 | __Pour aller plus loin__ 987 | 988 | * [Catamorphisme](https://fr.wikipedia.org/wiki/Catamorphisme) sur Wikipédia 989 | 990 | ### Anamorphisme 991 | 992 | Un anamorphisme (_anamorphism_ en anglais) est une fonction qui construit une structure en appliquant de manière répétée 993 | une autre fonction à son argument. C’est le contraire d’un [catamorphisme](#catamorphisme) : l'anamorphisme construit 994 | une structure et le catamorphisme la décompose. 995 | 996 | ```js 997 | // génère un tableau à partir d’une fonction et d’une valeur initiale. 998 | const deplier = (f, valeurInitiale) => { 999 | function appliquer (f, valeurInitiale, acc) { 1000 | const res = f(valeurInitiale) 1001 | return res ? appliquer(f, res[1], acc.concat([res[0]])) : acc 1002 | } 1003 | return appliquer(f, valeurInitiale, []) 1004 | } 1005 | 1006 | const compteARebour = n => deplier((n) => { 1007 | return n <= 0 ? undefined : [n, n - 1] 1008 | }, n) 1009 | 1010 | compteARebour(5) // [5, 4, 3, 2, 1] 1011 | ``` 1012 | 1013 | __Pour aller plus loin__ 1014 | 1015 | * [Anamorphisme](https://fr.wikipedia.org/wiki/Anamorphisme) sur Wikipédia 1016 | 1017 | ### Hylomorphisme 1018 | 1019 | Un hylomorphisme (_hylomorphism_ en anglais) est une fonction récursive composée d’un [anamorphisme](#anamorphisme) 1020 | suivi d’un [catamorphisme](#catamorphisme). 1021 | 1022 | ```js 1023 | const sommeJusquaX = (x) => somme(compteARebour(x)) 1024 | sommeJusquaX(5) // 15 1025 | ``` 1026 | 1027 | __Pour aller plus loin__ 1028 | 1029 | * [Hylomorphism (en)](https://en.wikipedia.org/wiki/Hylomorphism_(computer_science)) sur Wikipédia 1030 | 1031 | ### Paramorphisme 1032 | 1033 | Un paramorphisme (_paramorphism_ en anglais) est une fonction similaire [`reduceRight`](#catamorphisme), bien qu’il y 1034 | ait une différence. Dans un paramorphisme, les arguments du réducteur sont la valeur actuelle, la réduction de toutes 1035 | les valeurs précédentes et la liste des valeurs qui ont formé cette réduction. 1036 | 1037 | ```js 1038 | // Dangereux avec des listes contenant `undefined`, mais suffisant pour notre exemple. 1039 | const paramorphisme = (reducteur, accumulateur, elements) => { 1040 | if (elements.length === 0) { return accumulateur } 1041 | 1042 | const premierElement = elements[0] 1043 | const autresElements = elements.slice(1) 1044 | 1045 | return reducteur(premierElement, autresElements, paramorphisme(reducteur, accumulateur, autresElements)) 1046 | } 1047 | 1048 | const suffixes = liste => paramorphisme( 1049 | (x, xs, suffxs) => [xs, ...suffxs], 1050 | [], 1051 | liste 1052 | ) 1053 | 1054 | suffixes([1, 2, 3, 4, 5]) // [[2, 3, 4, 5], [3, 4, 5], [4, 5], [5], []] 1055 | ``` 1056 | 1057 | Le troisième paramètre du réducteur (dans l’exemple ci-dessus, `[x, ... xs]`) donne une sorte d’historique de ce qui a 1058 | conduit à la valeur `accumulateur` actuelle. 1059 | 1060 | __Pour aller plus loin__ 1061 | 1062 | * [Paramorphism (en)](https://en.wikipedia.org/wiki/Paramorphism) sur Wikipédia 1063 | 1064 | ### Apomorphisme 1065 | 1066 | L'apomorphisme (_apomorphism_ en anglais) est le contraire du paramorphisme, tout comme l’anamorphisme est celui du 1067 | catamorphisme. Avec le paramorphisme, l’accès à l’accumulateur et à ce qui a été accumulé sont conservés. L’apomorphisme 1068 | permet de _déplier (`unfold`)_ et laisse la possibilité de retourner une valeur plus tôt. 1069 | 1070 | __Pour aller plus loin__ 1071 | 1072 | * [Apomorphism (en)](https://en.wikipedia.org/wiki/Apomorphism) sur Wikipédia 1073 | 1074 | ## Setoïde 1075 | 1076 | Un setoïde (_setoid_ en anglais) est un objet qui a une fonction `equals` qui peut être utilisée pour le comparer à 1077 | d’autres objets du même type. 1078 | 1079 | Pour faire de `array` un setoïde : 1080 | 1081 | ```js 1082 | Array.prototype.equals = function (arr) { 1083 | const taille = this.length 1084 | if (taille !== arr.length) { 1085 | return false 1086 | } 1087 | for (let i = 0; i < taille; i++) { 1088 | if (this[i] !== arr[i]) { 1089 | return false 1090 | } 1091 | } 1092 | return true 1093 | } 1094 | 1095 | ;[1, 2].equals([1, 2]) // true 1096 | ;[1, 2].equals([0]) // false 1097 | ``` 1098 | 1099 | ## Demi-groupe 1100 | 1101 | Un demi-groupe (aussi appelé semi-groupe, ou _semigroup_ en anglais), est un objet doté d’une fonction `concat` qui 1102 | permet de le combiner avec un autre objet du même type. 1103 | 1104 | ```js 1105 | ;[1].concat([2]) // [1, 2] 1106 | ``` 1107 | 1108 | __Pour aller plus loin__ 1109 | 1110 | * [Demi-groupe](https://fr.wikipedia.org/wiki/Demi-groupe) sur Wikipédia 1111 | 1112 | ## Foldable 1113 | 1114 | Un _foldable_ (terme anglais) est un objet doté d’une fonction `reduce` qui applique une fonction à un accumulateur et à 1115 | chaque élément du tableau (de gauche à droite) pour le réduire à une seule valeur. 1116 | 1117 | ```js 1118 | const somme = (liste) => liste.reduce((acc, val) => acc + val, 0) 1119 | somme([1, 2, 3]) // 6 1120 | ``` 1121 | 1122 | ## Lentille 1123 | 1124 | Une lentille (_lens_ en anglais) est une structure (souvent un objet ou une fonction) qui associe un _getter_ et un 1125 | _setter_ non mutable et est utilisé sur d’autres structures. 1126 | 1127 | ```js 1128 | // En utilisant les [lentilles de Ramda (en)](https://ramdajs.com/docs/#lens) 1129 | const lentilleSurLeNom = R.lens( 1130 | // getter pour la propriété nom 1131 | (obj) => obj.nom, 1132 | // setter pour la propriété nom 1133 | (val, obj) => Object.assign({}, obj, { nom: val }) 1134 | ) 1135 | ``` 1136 | 1137 | Le fait d’avoir des accesseurs pour une structure donnée permet quelques fonctionnalités clés. 1138 | 1139 | ```js 1140 | const personne = { nom: 'Gertrude Blanch' } 1141 | 1142 | // utilisation du getter 1143 | R.view(lentilleNom, personne) // 'Gertrude Blanch' 1144 | 1145 | // utilisation du setter 1146 | R.set(lentilleNom, 'Shafi Goldwasser', personne) // {nom: 'Shafi Goldwasser'} 1147 | 1148 | // applique la fonction `majuscule` sur la valeur de la structure 1149 | R.over(lentilleNom, majuscule, personne) // {nom: 'GERTRUDE BLANCH'} 1150 | ``` 1151 | 1152 | Les lentilles sont aussi composables. Cela permet d’effectuer facilement des mises à jour de données profondément 1153 | imbriquées. 1154 | 1155 | ```js 1156 | // Cette lentille se concentre sur le premier élément d’un tableau non vide 1157 | const lentillePremierElement = R.lens( 1158 | // getter sur le premier élément du tableau 1159 | elements => elements[0], 1160 | // setter non mutable du premier élément du tableau 1161 | (val, [__, ...elements]) => [val, ...elements] 1162 | ) 1163 | 1164 | const personnes = [{ nom: 'Gertrude Blanch' }, { nom: 'Shafi Goldwasser' }] 1165 | 1166 | // Malgré ce que vous pourriez supposer, les lentilles se composent de gauche à droite. 1167 | R.over(compose(lentillePremierElement, lentilleNom), majuscule, personnes) // [{'nom': 'GERTRUDE BLANCH'}, {'nom': 'Shafi Goldwasser'}] 1168 | ``` 1169 | 1170 | Autres implémentations : 1171 | 1172 | * [partial.lenses (en)](https://github.com/calmm-js/partial.lenses) 1173 | * [nanoscope (en)](http://www.kovach.me/nanoscope/) 1174 | 1175 | ## Signatures de type 1176 | 1177 | Souvent, les fonctions en JavaScript incluent des commentaires qui indiquent les types de leurs arguments et les valeurs 1178 | de retour. C'est leur signature de type (_type signature_ en anglais). 1179 | 1180 | Les pratiques varient beaucoup au sein de la communauté, mais ils suivent souvent les modèles suivants : 1181 | 1182 | ```js 1183 | // nomFonction :: premierType -> secondType -> typeDeRetour 1184 | 1185 | // ajouter :: Number -> Number -> Number 1186 | const ajouter = (x) => (y) => x + y 1187 | 1188 | // incrementer :: Number -> Number 1189 | const incrementer = (x) => x + 1 1190 | ``` 1191 | 1192 | Si une fonction accepte une autre fonction comme argument, elle est placée entre parenthèses : 1193 | 1194 | ```js 1195 | // appeler :: (a -> b) -> a -> b 1196 | const appeler = (f) => (x) => f(x) 1197 | ``` 1198 | 1199 | Les lettres `a`, `b`, `c`, `d` sont utilisée pour signifier que l’argument peut être de n’importe quel type. La version 1200 | suivante de `map` prend une fonction qui transforme une valeur d’un type `a` en un autre type `b`, un tableau de valeurs 1201 | de type `a`, et retourne un tableau de valeurs de type `b`. 1202 | 1203 | ```js 1204 | // map :: (a -> b) -> [a] -> [b] 1205 | const map = (f) => (liste) => liste.map(f) 1206 | ``` 1207 | 1208 | __Pour aller plus loin__ 1209 | 1210 | * [Signature de type](https://fr.wikipedia.org/wiki/Signature_de_type) sur Wikipédia 1211 | * [Ramda’s type signatures (en)](https://github.com/ramda/ramda/wiki/Type-Signatures) 1212 | * [Mostly Adequate Guide (en)](https://drboolean.gitbooks.io/mostly-adequate-guide/content/ch7.html#whats-your-type) 1213 | * [What is Hindley-Milner? (en)](http://stackoverflow.com/a/399392/22425) sur Stack Overflow 1214 | 1215 | ## Type algébrique de données 1216 | 1217 | Un type algébrique de données (_algebraic data type_ en anglais) est un type composite fabriqué à partir de l’assemblage 1218 | d’autres types. Deux classes communes de types algébriques sont les [sommes](#types-somme) et les 1219 | [produits](#types-produit). 1220 | 1221 | __Pour aller plus loin__ 1222 | 1223 | * [Type algébrique de données](https://fr.wikipedia.org/wiki/Type_alg%C3%A9brique_de_donn%C3%A9es) sur Wikipédia 1224 | 1225 | ### Types somme 1226 | 1227 | Un type somme (_sum type_ en anglais) est l’union de deux types l’un avec l’autre. On l’appelle ainsi car le nombre de 1228 | valeurs possibles dans le type résultant est la somme des types d’entrée. 1229 | 1230 | JavaScript n’a pas de type somme, mais on peut néanmoins utiliser les `Set`s le simuler : 1231 | 1232 | ```js 1233 | // Imaginez que plutôt que des ensembles ici, nous avons des types qui ne peuvent prendre que ces valeurs. 1234 | const bools = new Set([true, false]) 1235 | const halfTrue = new Set(['half-true']) 1236 | 1237 | // Le type LogiqueFaible contient la somme des valeurs de bools et halfTrue 1238 | const valeursDeLogiqueFaible = new Set([...bools, ...halfTrue]) 1239 | ``` 1240 | 1241 | Les types somme sont parfois appelés _types union_ (_union types_ en anglais), _unions discriminées_ (_discriminated 1242 | unions_ en anglais) ou _unions étiquetées_ (_tagged unions_ en anglais). 1243 | 1244 | Il existe [plusieurs](https://github.com/paldepind/union-type) [bibliothèques](https://github.com/puffnfresh/daggy) en 1245 | JavaScript qui facilitent la définition et l’utilisation des types somme. 1246 | 1247 | Flow défini [des types union](https://flow.org/en/docs/types/unions/) et TypeScript 1248 | [des énumérations](https://www.typescriptlang.org/docs/handbook/enums.html) pour jouer ce rôle. 1249 | 1250 | __Pour aller plus loin__ 1251 | 1252 | * [Type somme](https://fr.wikipedia.org/wiki/Type_alg%C3%A9brique_de_donn%C3%A9es#Type_somme) sur Wikipédia 1253 | 1254 | ### Types produit 1255 | 1256 | Un type produit (_product type_ en anglais) combine des types d’une manière que vous connaissez probablement mieux : 1257 | 1258 | ```js 1259 | // point :: (Number, Number) -> {x: Number, y: Number} 1260 | const point = (x, y) => ({ x, y }) 1261 | ``` 1262 | 1263 | L’exemple précédent est ce qu’on appelle un produit car l’ensemble des valeurs possibles de la structure de données est 1264 | le produit des différentes valeurs. De nombreux languages ont un type tuple qui est la formulation la plus simple d’un 1265 | type produit. 1266 | 1267 | __Pour aller plus loin__ 1268 | 1269 | * [Type produit](https://fr.wikipedia.org/wiki/Type_alg%C3%A9brique_de_donn%C3%A9es#Type_produit) sur Wikipédia 1270 | * [Théorie des ensembles](https://fr.wikipedia.org/wiki/Th%C3%A9orie_des_ensembles) sur Wikipédia 1271 | 1272 | ## Option 1273 | 1274 | L’option (terme identique en anglais) est un [type somme](#types-somme) qui encapsule deux cas souvent appelés 1275 | `Some` et `None`. Les options sont utiles avec des fonctions qui peuvent ne pas renvoyer de valeur. 1276 | 1277 | ```js 1278 | // Définition naïve 1279 | 1280 | const Some = (v) => ({ 1281 | val: v, 1282 | map (f) { 1283 | return Some(f(this.val)) 1284 | }, 1285 | chain (f) { 1286 | return f(this.val) 1287 | } 1288 | }) 1289 | 1290 | const None = () => ({ 1291 | map (f) { 1292 | return this 1293 | }, 1294 | chain (f) { 1295 | return this 1296 | } 1297 | }) 1298 | 1299 | // optionnel :: (String, {a}) -> Option a 1300 | const optionnel = (key, obj) => typeof obj[key] === 'undefined' ? None() : Some(obj[key]) 1301 | ``` 1302 | 1303 | Utilisez `chain` pour enchaîner des fonctions qui retournent des `Option`s: 1304 | 1305 | ```js 1306 | 1307 | // getArticle :: Panier -> Option Article 1308 | const getArticle = (panier) => optionnel('article', panier) 1309 | 1310 | // getPrix :: Article -> Option Number 1311 | const getPrix = (article) => optionnel('prix', article) 1312 | 1313 | // getPrixImbrique :: cart -> Option a 1314 | const getPrixImbrique = (panier) => getArticle(panier).chain(getPrix) 1315 | 1316 | getPrixImbrique({}) // None() 1317 | getPrixImbrique({ item: { foo: 1 } }) // None() 1318 | getPrixImbrique({ item: { price: 9.99 } }) // Some(9.99) 1319 | ``` 1320 | 1321 | `Option` est aussi connu sous le nom `Maybe`. `Some` est parfois appelé `Just`, et `None` est parfois appelé `Nothing`. 1322 | 1323 | ## Fonction 1324 | 1325 | En programmation fonctionnelle, une fonction (ou expression lambda) `f :: A => B` est une expression avec un seul 1326 | argument immuable de type `A`et une seule valeur de retour de type `B`. La valeur de retour dépend entièrement de 1327 | l’argument, ce qui rend la fonction [référentiellement transparente](#transparence-référentielle). Cela implique donc 1328 | que la fonction ne produit aucun [effet de bord](#effets-de-bord) caché et qu'elle est [pure](#fonction-pure) par 1329 | définition. Ces propriétés rendent les fonctions agréables à manipuler : elles sont complètement déterministes et ainsi 1330 | prévisibles. Les fonctions permettent d’utiliser le code comme des données, en faisant abstraction du comportement : 1331 | 1332 | ```js 1333 | // fois2 :: Number -> Number 1334 | const fois2 = n => n * 2 1335 | 1336 | ;[1, 2, 3].map(fois2) // [2, 4, 6] 1337 | ``` 1338 | 1339 | ## Fonction partielle 1340 | 1341 | Une fonction partielle est une [function](#fonction) qui n’est pas définie pour tous les arguments - elle peut renvoyer 1342 | un résultat inattendu ou ne jamais se terminer. Les fonctions partielles ajoutent de la surcharge cognitive, elles sont 1343 | plus difficiles à appréhender et peuvent entraîner des erreurs d’exécution. 1344 | 1345 | ```js 1346 | // exemple 1 : somme d’une liste 1347 | // somme :: [Number] -> Number 1348 | const somme = nombres => nombres.reduce((a, b) => a + b) 1349 | somme([1, 2, 3]) // 6 1350 | somme([]) // TypeError: Reduce of empty array with no initial value 1351 | 1352 | // exemple 2 : récupération du premier élément d’une liste 1353 | // premier :: [A] -> A 1354 | const premier = a => a[0] 1355 | premier([42]) // 42 1356 | premier([]) // undefined 1357 | // ou pire 1358 | premier([[42]])[0] // 42 1359 | premier([])[0] // Uncaught TypeError: Cannot read property '0' of undefined 1360 | 1361 | // exemple 3 : repéter une fonction N fois 1362 | // repeter :: Number -> (Number -> Number) -> Number 1363 | const repeter = n => fn => n && (fn(n), times(n - 1)(fn)) 1364 | repeter(3)(console.log) 1365 | // 3 1366 | // 2 1367 | // 1 1368 | repeter(-1)(console.log) // RangeError: Maximum call stack size exceeded 1369 | ``` 1370 | 1371 | ### Manipuler des fonctions partielles 1372 | 1373 | Les fonctions partielles sont dangereuses et doivent être utilisées avec prudence. Vous pouvez en effet obtenir un 1374 | résultat inattendu (erroné), rencontrer des erreurs d’exécution, ou même s’exécuter indéfiniment lorsque vous les 1375 | utilisez. Connaitre et traiter tous ces cas aux limites peut donc devenir très fastidieux. 1376 | 1377 | Heureusement, il est possible de transformer une fonction partielle en fonction traditionnelle (ou fonction totale). 1378 | On peut en effet utiliser des valeurs par défaut ou des garde-fous pour traiter les entrées pour lesquelles la fonction 1379 | partielle n’a pas de comportement défini. En utilisant par exemple le type [Option](#Option), nous pouvons obtenir 1380 | soit `Some(value)` ou `None` là où la fonction se serait comportée de manière inattendue. 1381 | 1382 | ```js 1383 | // exemple 1: somme d’une liste 1384 | // on peut fournir une valeur par défaut afin que la fonction renvoie toujours un résultat 1385 | // somme :: [Number] -> Number 1386 | const somme = arr => arr.reduce((a, b) => a + b, 0) 1387 | somme([1, 2, 3]) // 6 1388 | somme([]) // 0 1389 | 1390 | // exemple 2: récupération du premier élément d’une liste 1391 | // on peut utiliser le type Option 1392 | // premier :: [A] -> Option A 1393 | const premier = a => a.length ? Some(a[0]) : None() 1394 | premier([42]).map(a => console.log(a)) // 42 1395 | premier([]).map(a => console.log(a)) // console.log ne s’exécutera pas 1396 | // et avec le pire scénario 1397 | premier([[42]]).map(a => console.log(a[0])) // 42 1398 | premier([]).map(a => console.log(a[0])) // console.log ne s’exécutera pas, et il n’y aura pas d’erreur 1399 | // De plus, vous saurez grâce au type de retour (Option) que vous devez utiliser `.map` 1400 | // pour accèder à la valeur de retour et vous n’oublierez plus de vérifier l’argument 1401 | // car cela devient obligatoire. 1402 | 1403 | // exemple 3: repéter une fonction N fois 1404 | // on fait en sorte que la fonction se termine toujours en changeant les conditions 1405 | // repeter :: Number -> (Number -> Number) -> Number 1406 | const repeter = n => fn => n > 0 && (fn(n), times(n - 1)(fn)) 1407 | repeter(3)(console.log) 1408 | // 3 1409 | // 2 1410 | // 1 1411 | repeter(-1)(console.log) // ne fera rien 1412 | ``` 1413 | 1414 | En rendant vos fonctions partielles totales, les erreurs d’exécution peuvent être évités. Toujours renvoyer une valeur 1415 | permet de plus d’obtenir du code qui est à la fois plus facile à comprendre et à maintenir. 1416 | 1417 | ## Fonction totale 1418 | 1419 | Une fonction partielle est une [function](#fonction) qui renvoie un résultat valide pour toutes les valeurs d’entrées 1420 | possibles. Les fonctions totales s’opposent aux [fonctions partielles](#fonction-partielle) qui peuvent générer des 1421 | erreurs, renvoyer un résultat inattendu ou s’exécuter indéfiniment. 1422 | 1423 | ## Bibliothèques de programmation fonctionnelle en JavaScript 1424 | 1425 | * [mori](https://github.com/swannodette/mori) 1426 | * [Immutable](https://github.com/facebook/immutable-js/) 1427 | * [Immer](https://github.com/mweststrate/immer) 1428 | * [Ramda](https://github.com/ramda/ramda) 1429 | * [ramda-adjunct](https://github.com/char0n/ramda-adjunct) 1430 | * [ramda-extension](https://github.com/tommmyy/ramda-extension) 1431 | * [Folktale](http://folktale.origamitower.com/) 1432 | * [monet.js](https://cwmyers.github.io/monet.js/) 1433 | * [lodash](https://github.com/lodash/lodash) 1434 | * [Underscore.js](https://github.com/jashkenas/underscore) 1435 | * [Lazy.js](https://github.com/dtao/lazy.js) 1436 | * [maryamyriameliamurphies.js](https://github.com/sjsyrek/maryamyriameliamurphies.js) 1437 | * [Haskell in ES6](https://github.com/casualjavascript/haskell-in-es6) 1438 | * [Sanctuary](https://github.com/sanctuary-js/sanctuary) 1439 | * [Crocks](https://github.com/evilsoft/crocks) 1440 | * [Fluture](https://github.com/fluture-js/Fluture) 1441 | * [fp-ts](https://github.com/gcanti/fp-ts) 1442 | 1443 | --- 1444 | 1445 | __P.S.__ : Merci à toutes les personnes ayant contribué 1446 | [au dépôt d’origine](https://github.com/hemanth/functional-programming-jargon/graphs/contributors) comme à 1447 | [ce dépôt](https://github.com/marcwrobel/functional-programming-jargon-fr/graphs/contributors) ! 1448 | --------------------------------------------------------------------------------