Introduction à la programmation fonctionnelle de niveau avancé
\n\nLa programmation fonctionnelle (PF) est un paradigme qui considère les calculs comme l'exécution de fonctions mathématiques et évite les changements d'état et les données mutables. En JavaScript, en particulier dans l'environnement Node.js, la PF devient de plus en plus populaire grâce à sa prévisibilité, sa testabilité et son parallélisme.
\n\nSi vous maîtrisez déjà les bases de la PF (fonctions pures, fonctions d'ordre supérieur, map, filter, reduce), il est temps de passer au niveau supérieur. Dans cet article, nous aborderons des concepts avancés : la curryfication, la composition, les monades et le travail avec des données immuables.
1. Curryfication et application partielle
\n\nQu'est-ce que la curryfication ?
\nLa curryfication est le processus de transformation d'une fonction qui prend plusieurs arguments en une séquence de fonctions, chacune prenant un seul argument. Cela permet de créer des fonctions spécialisées à partir de fonctions générales.
\n\n// Fonction normale\nconst add = (a, b, c) => a + b + c;\nconsole.log(add(1, 2, 3)); // 6\n\n// Version curryfiée\nconst curriedAdd = a => b => c => a + b + c;\nconsole.log(curriedAdd(1)(2)(3)); // 6\n\n// Fonction de curryfication universelle\nconst curry = (fn) => {\n const arity = fn.length;\n const resolver = (...args) => {\n if (args.length < arity) {\n return (...nextArgs) => resolver(...args, ...nextArgs);\n }\n return fn(...args);\n };\n return resolver;\n};\n\nconst multiply = (a, b, c) => a * b * c;\nconst curriedMultiply = curry(multiply);\nconsole.log(curriedMultiply(2)(3)(4)); // 24\nconsole.log(curriedMultiply(2, 3)(4)); // 24\n\nApplication partielle
\nL'application partielle est la fixation d'une partie des arguments d'une fonction, créant une nouvelle fonction avec moins d'arguments. Contrairement à la curryfication, l'application partielle n'exige pas une séquence d'appels stricte.
\n\nconst partial = (fn, ...presetArgs) => (...laterArgs) => fn(...presetArgs, ...laterArgs);\n\nconst greet = (greeting, name) => `${greeting}, ${name}!`;\nconst sayHello = partial(greet, 'Hello');\nconst sayHi = partial(greet, 'Hi');\n\nconsole.log(sayHello('Alice')); // Hello, Alice!\nconsole.log(sayHi('Bob')); // Hi, Bob!\n\n2. Composition de fonctions
\n\nComposition avancée
\nLa composition est le processus de combinaison de plusieurs fonctions pour créer une nouvelle fonction. En PF avancée, nous utilisons la composition de droite à gauche (compose) et de gauche à droite (pipe).
// Composition simple (de droite à gauche)\nconst compose = (...fns) => (value) => fns.reduceRight((acc, fn) => fn(acc), value);\n\n// Composition de gauche à droite (pipe)\nconst pipe = (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value);\n\n// Exemple : traitement d'une chaîne\nconst trim = (str) => str.trim();\nconst toLowerCase = (str) => str.toLowerCase();\nconst capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);\nconst removeSpaces = (str) => str.replace(/\\s+/g, '-');\n\nconst slugify = compose(capitalize, toLowerCase, trim, removeSpaces);\n// Équivalent : removeSpaces(trim(toLowerCase(capitalize(" Hello World "))))\n\nconsole.log(slugify(' Hello World ')); // Hello-world\n\nTransducteurs (Transducers)
\nLes transducteurs sont une composition de fonctions de transformation qui fonctionnent avec n'importe quelle structure de données itérable. Ils permettent de combiner plusieurs opérations (par exemple, map et filter) en un seul passage.
const transducer = (mapper, predicate) => (reducer) => (acc, value) => {\n const mapped = mapper(value);\n return predicate(mapped) ? reducer(acc, mapped) : acc;\n};\n\nconst double = (x) => x * 2;\nconst isEven = (x) => x % 2 === 0;\n\nconst processNumbers = transducer(double, isEven);\nconst sumReduc