| Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente |
| cnam:utc503:declarative:fonctionnelle [2023/11/06 19:33] – [1 - fonctions pures] jcheron | cnam:utc503:declarative:fonctionnelle [2025/10/13 13:29] (Version actuelle) – jcheron |
|---|
| Basée sur l'utilisation des fonctions, à la condition de respecter certains principes. | Basée sur l'utilisation des fonctions, à la condition de respecter certains principes. |
| |
| La prog fonctionnelle n'admet pas le changement d'états et la mutation des données(contrairement à la prog impérative). | La prog fonctionnelle n'admet pas le changement d'états et la mutation des données (contrairement à la prog impérative). |
| |
| Les langages fonctionnels sont ceux vouent un culte à ces principes ou sont basés sur eux : | Les langages fonctionnels sont ceux vouent un culte à ces principes ou sont basés sur eux : |
| <WRAP round box> | <WRAP round info box> |
| Lisp (1958), Scheme (1975), Common Lisp (1984), Haskell (1987), OCaml (1996), Scala (2003), PureScript (2013)... | Lisp (1958), Scheme (1975), Common Lisp (1984), Haskell (1987), OCaml (1996), Scala (2003), PureScript (2013)... |
| </WRAP> | </WRAP> |
| Les langages de programmation impératifs acceptant le passage de fonctions en paramètres peuvent être utilisés dans le cadre d'une approche fonctionnelle : | Les langages de programmation impératifs acceptant le passage de fonctions en paramètres peuvent être utilisés dans le cadre d'une approche fonctionnelle : |
| |
| <WRAP round box> | <WRAP round info box> |
| ECMAScript, Java, C#, PHP, Perl, Python, Ruby, Kotlin... | ECMAScript, Java, C#, PHP, Perl, Python, Ruby, Kotlin... |
| </WRAP> | </WRAP> |
| let pi = 3.14 ; | let pi = 3.14 ; |
| const calculateArea = (radius) => radius * radius * pi; | const calculateArea = (radius) => radius * radius * pi; |
| calculateArea(10);// returns 314.0 | calculateArea(10);// returns 314.0, ou autre chose si pi est modifié |
| </sxh> | </sxh> |
| == Suppression de la variable globale PI pour la rendre pure : == | == Suppression de la variable globale **pi** pour la rendre pure : == |
| <sxh javascript;gutter:false> | <sxh javascript;gutter:false> |
| let pi = 3.14; | let pi = 3.14; |
| calculateArea(10, pi);// returns 314.0 | calculateArea(10, pi);// returns 314.0 |
| </sxh> | </sxh> |
| | == Ou (plus logique), passage de **pi** en constante : == |
| | <sxh javascript;gutter:false> |
| | const PI = 3.14; |
| | const calculateArea = (radius) => radius * radius * PI; |
| | calculateArea(10);// returns 314.0 |
| | </sxh> |
| | |
| | === b- Influence du contexte === |
| | Exemples de fonctions impures : |
| | == Lecture de fichiers : == |
| | <sxh javascript;gutter:false> |
| | const charactersCounter = (text) => `Character count: ${text.length}`; |
| | |
| | function analyzeFile(filename) { |
| | let fileContent = open(filename); |
| | return charactersCounter(fileContent); |
| | } |
| | </sxh> |
| | |
| | == Génération d'aléatoires : == |
| | <sxh javascript;gutter:false> |
| | function getRandomArbitrary(min, max) { |
| | return Math.random() * (max - min) + min; |
| | } |
| | </sxh> |
| | |
| | === c- Mutabilité === |
| | Exemple de fonction impure : |
| | == Modification de globale : == |
| | <sxh javascript;gutter:false> |
| | let counter = 1; |
| | function increaseCounter(value) { |
| | counter = value + 1; |
| | } |
| | increaseCounter(counter); |
| | console.log(counter);// 2 |
| | </sxh> |
| | |
| | Rendue pure : |
| | |
| | <sxh javascript;gutter:false> |
| | let counter = 1; |
| | const increaseCounter = (value) => value + 1; |
| | increaseCounter(counter);// 2 |
| | console.log(counter);// 1 |
| | </sxh> |
| | |
| | Avantages : |
| | * Stabilité, prévisibilité |
| | * Testabilité (unitaire) |
| | * Réutilisation (sans effets de bord) |
| | |
| | ==== 2- Immutabilité ==== |
| | |
| | Les données doivent être immutables, c'est-à-dire que leur valeur ne peut changer après initialisation. |
| | |
| | === Exemple d'itération avec variables mutables : === |
| | |
| | |
| | <sxh javascript;gutter:false> |
| | let values = [1, 2, 3, 4, 5]; |
| | let sumOfValues = 0; |
| | for (let i = 0; i < values.length; i++) { |
| | sumOfValues += values[i]; |
| | } |
| | sumOfValues // 15 |
| | </sxh> |
| | |
| | === Version immutable obtenue par récursion : === |
| | |
| | |
| | <sxh javascript;gutter:false> |
| | let list = [1, 2, 3, 4, 5]; |
| | let accumulator = 0; |
| | function sum(list, accumulator) { |
| | if (list.length == 0) { |
| | return accumulator; |
| | } |
| | return sum(list.slice(1), accumulator + list[0]); |
| | } |
| | sum(list, accumulator); // 15 |
| | list; // [1, 2, 3, 4, 5] |
| | accumulator; // 0 |
| | </sxh> |
| | |
| | === Avantages : === |
| | |
| | |
| | * L'exécution d'un prog n'est plus dépendant des changements d'état |
| | |
| | ==== 3- Transparence référentielle ==== |
| | |
| | Referential transparency |
| | |
| | La transparence référentielle est obtenue grâce aux fonctions pures, et elle consiste à permettre le remplacement d'un appel d'une fonction pure, par la valeur qu'elle retourne,sans perturber le fonctionnement du programme. |
| | |
| | **Exemple :** |
| | |
| | Soit la fonction carré suivante : |
| | <sxh javascript;gutter:false> |
| | const square = (n) => n * n; |
| | </sxh> |
| | |
| | Pour la même valeur en entrée, cette fonction pure retournera toujours la même valeur : |
| | |
| | <sxh javascript;gutter:false> |
| | square(2);// 4 |
| | square(2);// 4 |
| | square(2);// 4 |
| | // ... |
| | </sxh> |
| | |
| | Avec le paramètre ''2'', la fonction ''square'' retourne toujours ''4''. Il est donc possible deremplacer ''square(2)'' par ''4'' : Notre fonction est donc référentiellement transparente. |
| | |
| | <WRAP info> |
| | ''pure functions + immutable data = referential transparency'' |
| | |
| | Grâce à ce concept, il est possible de memoïzer (https://en.wikipedia.org/wiki/Memoization) la fonction, pour accroître les performances, en évitant son appel. |
| | </WRAP> |
| | |
| | ==== 4- Fonction d'ordre supérieur ==== |
| | |
| | Fonctions objet de première classe ou Functions as first-class entities : |
| | |
| | Une fonction est un type de données (callback) et peut-être passée en paramètre d'une fonction ou être retournée par une fonction. |
| | |
| | Les fonctions qui en manipulent d'autres sont qualifiées d'ordre supérieur. |
| | |
| | **Exemple :** |
| | |
| | Considérons les fonctions suivantes : |
| | |
| | <sxh javascript;gutter:false> |
| | const doubleSum = (a, b) => (a + b) * 2; |
| | const doubleSubtraction = (a, b) => (a - b) * 2; |
| | </sxh> |
| | |
| | Nous pourrions considérer que nous avons 3 fonctions : |
| | * somme |
| | * soustraction |
| | * double qui peut utiliser la soustraction, la somme ou toute autre opération |
| | |
| | <sxh javascript;gutter:false> |
| | const |
| | sum = (a, b) => a + b; |
| | const subtraction = (a, b) => a - b; |
| | const doubleOperator = (f, a, b) => f(a, b) * 2; |
| | |
| | doubleOperator(sum, 3, 1);// 8 |
| | doubleOperator(subtraction, 3, 1);// 4 |
| | </sxh> |
| | |
| | **doubleOperator** est une fonction d'ordre supérieur. |
| | |
| | ==== 5- Monades ==== |
| |
| | Structure permettant de manipuler des langages fonctionnels purs dans des traits impératifs. |
| |
| | **Exemples :** |
| |
| | * Result may or may not exist: solved by the Maybe monad. |
| | * Nondeterministic number of result: solved by the List monad. |
| | * Outside world interaction: solved by the IO monad. |
| | * Eventual result: solved by the Promise/Future monad. |
| | * Dependence on state: solved by the State monad. |
| | * Errors: solved by the Error monad. |
| |
| | ===== Applications ===== |
| |
| | * [[https://www.codingame.com/playgrounds/2980/practical-introduction-tofunctional- |
| | programming-with-js/pure-functions|Exercices pratiques sur Coding game]] |
| |