Curryfication et application partielle en JavaScript
La curryfication et l'application partielle sont des techniques de programmation fonctionnelle qui améliorent la modularité et la réutilisabilité de votre code JavaScript.
La curryfication et l'application partielle sont de puissantes techniques de programmation fonctionnelle qui peuvent améliorer considérablement la modularité et la réutilisabilité de votre code JavaScript. Toutes deux reposent sur les fermetures — la capacité d'une fonction interne à mémoriser les variables de la fonction externe qui l'a créée — ainsi une fonction retournée conserve les arguments que vous avez déjà fournis.
Cet article couvre ce qu'est la curryfication et pourquoi elle est importante, comment écrire un utilitaire curry réutilisable, en quoi l'application partielle diffère de la curryfication, des cas d'utilisation concrets, ainsi que les bonnes pratiques (et les pièges) à garder à l'esprit.
Comprendre la curryfication
La curryfication est une technique par laquelle une fonction est transformée en une séquence de fonctions, chacune prenant un seul argument. Elle vous permet de décomposer une fonction qui prend plusieurs arguments en une série de fonctions unaires (à un seul argument).
Exemple de curryfication
Considérons une fonction simple qui additionne trois nombres :
function add(a, b, c) {
return a + b + c;
}Nous pouvons transformer cette fonction en une version currifiée :
Explication :
- La fonction
addprend trois arguments et retourne leur somme. - La fonction
curryAddest une version currifiée deadd. Elle prend un argumentaet retourne une autre fonction qui prendb. Cette deuxième fonction retourne encore une autre fonction qui prendc. La fonction la plus interne retourne la somme dea,betc.
Avantages de la curryfication
- Réutilisabilité : Les fonctions currifiées vous permettent de créer de nouvelles fonctions en fixant certains arguments. Cela améliore la réutilisabilité en vous permettant de créer facilement des versions spécialisées d'une fonction sans dupliquer le code.
Ici, curriedAdd est une version currifiée de la fonction add. Elle nous permet de créer des fonctions spécialisées comme add5 en fixant le premier argument (a) à 5. Cela favorise la réutilisation du code car nous pouvons créer plusieurs fonctions spécialisées sans répéter la logique d'addition.
- Composition de fonctions : La curryfication facilite la composition de fonctions. La composition de fonctions est le processus de combinaison de deux fonctions ou plus pour produire une nouvelle fonction.
Dans cet exemple, nous composons les fonctions multiply et addOne grâce à la curryfication. La forme concise a => b => a * b s'appuie sur les fonctions fléchées, qui rendent les fonctions unaires imbriquées faciles à lire. En curryfiant ces fonctions, nous pouvons facilement créer une nouvelle fonction addOneThenMultiplyBy5, qui ajoute d'abord 1 à l'entrée (x) puis multiplie le résultat par 5. Cela illustre comment la curryfication facilite la composition de fonctions, simplifiant la création de nouvelles fonctions par combinaison de fonctions existantes.
Implémenter la curryfication en JavaScript
Nous pouvons créer une fonction utilitaire pour curryfier n'importe quelle fonction. Voici une implémentation d'une fonction de curryfication générique :
Explication :
-
Fonction de curryfication (
curry) :- La fonction
curryprend une autre fonctionfnen entrée. - Elle retourne une nouvelle fonction appelée
curried. - Cette fonction
curriedaccepte un nombre quelconque d'arguments grâce à la syntaxe du paramètre rest (...args).
- La fonction
-
Fonction currifiée (
curried) :- À l'intérieur de
curried, elle vérifie si le nombre d'arguments fournis (args.length) est supérieur ou égal au nombre d'arguments attendus par la fonction originalefn(fn.length). - Si suffisamment d'arguments sont fournis, elle appelle la fonction originale
fnavec ces arguments viafn.apply(this, args). - Si le nombre d'arguments est insuffisant, elle retourne une nouvelle fonction qui accepte davantage d'arguments (
nextArgs) grâce à l'opérateur de décomposition (...nextArgs). - Cette nouvelle fonction appelle récursivement
curriedavec les arguments combinés (args.concat(nextArgs)), garantissant que tous les arguments sont éventuellement collectés avant d'appeler la fonction originalefn.
Remarque :
fn.lengthne comptabilise que les paramètres sans valeur par défaut et ignore les paramètres rest. - À l'intérieur de
-
Exemple d'utilisation :
- Nous définissons une fonction
multiplyqui prend trois arguments et retourne leur produit. - Nous créons une version currifiée de la fonction
multiplyen la passant à la fonctioncurry, qui retourne une nouvelle fonctioncurriedMultiply. - Désormais,
curriedMultiplypeut être appelée avec un, deux ou trois arguments. - Chaque fois que nous appelons
curriedMultiplyavec un ou plusieurs arguments, elle retourne une nouvelle fonction jusqu'à ce que tous les arguments soient collectés, moment auquel elle retourne le résultat de la multiplication des arguments.
- Nous définissons une fonction
Explorer l'application partielle
L'application partielle est une technique par laquelle vous créez une nouvelle fonction en pré-remplissant certains arguments de la fonction originale. C'est particulièrement utile pour créer des fonctions spécialisées.
Exemple d'application partielle
Considérons la fonction suivante qui formate un message :
function formatMessage(greeting, name) {
return `${greeting}, ${name}!`;
}Nous pouvons créer une fonction partiellement appliquée :
Explication :
- La fonction
formatMessageprend deux arguments,greetingetname, et retourne un message formaté. - La fonction
partialprend une fonctionfnet quelques arguments prédéfinis (...presetArgs). Elle retourne une nouvelle fonction qui prend les arguments restants (...laterArgs). - Lorsque la nouvelle fonction est appelée, elle combine
presetArgsetlaterArgset appelle la fonction originalefnavec ces arguments. - En utilisant
partial, nous créonsgreetHello, une fonction qui utilise toujours "Hello" comme salutation. Lorsqu'elle est appelée avec un nom, elle retourne le message complet.
Avantages de l'application partielle
-
Simplification : Créer des fonctions plus simples à partir de fonctions plus complexes
Supposons que nous ayons une fonction qui calcule le prix final d'un article après application d'une remise et d'une taxe.
function calculateFinalPrice(price, discount, tax) {
return price - (price * discount) + (price * tax);
}Cette fonction nécessite trois arguments, ce qui la rend un peu fastidieuse à utiliser de manière répétée si les taux de remise et de taxe sont souvent identiques. Avec l'application partielle, nous pouvons simplifier cela.
Dans l'exemple ci-dessus, applyDiscountAndTax est une fonction partiellement appliquée qui prédéfinit les valeurs de discount et tax. Cela facilite le calcul du prix final pour différents articles sans avoir à spécifier à chaque fois les taux de remise et de taxe.
-
Réutilisabilité du code : Réutiliser la logique commune d'une fonction avec différents arguments prédéfinis
Imaginons que nous ayons une fonction qui journalise des messages avec différents niveaux de sévérité.
function logMessage(level, message) {
console.log(`[${level}] ${message}`);
}Nous pouvons créer des fonctions réutilisables pour différents niveaux de journalisation grâce à l'application partielle.
Ici, createLogger est une fonction partiellement appliquée qui fixe l'argument level. Les fonctions infoLogger et errorLogger peuvent désormais être utilisées pour journaliser des messages avec les niveaux prédéfinis, en réutilisant la logique commune de logMessage.
- Lisibilité améliorée : Rendre le code plus lisible en décomposant les fonctions complexes Considérons une fonction qui formate des dates dans différents styles.
function formatDate(date, format) {
const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
if (format === 'US') {
options.month = 'long';
} else if (format === 'EU') {
options.day = 'numeric';
options.month = 'numeric';
}
return new Date(date).toLocaleDateString(undefined, options);
}En utilisant l'application partielle, nous pouvons créer des fonctions plus lisibles pour différents formats de date.
createDateFormatter applique partiellement l'argument format, produisant des fonctions spécifiques pour les formats de date US et EU. Cette décomposition rend le code plus lisible et plus facile à comprendre, car chaque fonction de formatage est dédiée à un format particulier.
Ces exemples illustrent comment l'application partielle en JavaScript peut simplifier les fonctions complexes, améliorer la réutilisabilité du code et en améliorer la lisibilité, rendant le code plus facile à gérer et à comprendre.
Curryfication vs. Application partielle
Ces deux techniques sont étroitement liées et souvent confondues, mais elles ne sont pas identiques :
- La curryfication transforme une fonction de N arguments en N fonctions imbriquées prenant chacune exactement un argument. Une fonction entièrement currifiée est toujours appelée un argument à la fois :
f(a)(b)(c). - L'application partielle fixe certains arguments d'une fonction et retourne une nouvelle fonction qui prend le reste en un seul appel :
g = partial(f, a)puisg(b, c).
En résumé : la curryfication concerne la forme de la fonction (une chaîne d'appels unaires), tandis que l'application partielle concerne le pré-remplissage d'arguments. Notez que l'utilitaire générique curry présenté ci-dessus est plus flexible que la curryfication stricte — il accepte des arguments par groupes (curriedMultiply(2, 3)(4)), brouillant ainsi la frontière et supportant effectivement l'application partielle également.
Bonnes pratiques pour utiliser la curryfication et l'application partielle
Garder les fonctions pures
- Assurez-vous que les fonctions currifiées et partiellement appliquées restent pures, sans effets secondaires. Cela les rend plus faciles à raisonner et à tester.
Utiliser à bon escient
- Utilisez la curryfication et l'application partielle lorsqu'elles s'adaptent naturellement au problème en question.
- Évitez de surcharger votre code avec ces techniques, car elles peuvent le rendre plus difficile à comprendre si elles ne sont pas utilisées avec discernement.
Tirer parti de la composition de fonctions
- Combinez des fonctions currifiées pour créer des fonctionnalités plus complexes. La curryfication fonctionne bien avec la composition de fonctions, menant à un code plus modulaire.
Documentation et nommage
- Documentez correctement les fonctions currifiées et partiellement appliquées pour indiquer leur usage attendu.
- Utilisez des noms clairs et descriptifs pour les fonctions afin de transmettre leur objectif.
Attention à ces pièges
fn.lengthn'est pas fiable pour la curryfication automatique. Un utilitaire génériquecurryqui repose surfn.lengthse comportera mal avec des paramètres par défaut ou des paramètres rest, car ceux-ci ne sont pas comptabilisés. Si une fonction les utilise, passez l'arité attendue explicitement plutôt que de faire confiance àfn.length.- Une curryfication excessive nuit à la lisibilité. Une longue chaîne
f(a)(b)(c)(d)est plus difficile à lire qu'un seul appel bien nommé. Recourez à la curryfication lorsqu'elle élimine véritablement la répétition. - Attention à
this. Les fonctions fléchées n'ont pas leur proprethis, donc une chaîne currifiée écrite avec des flèches ne peut pas être utilisée comme méthode reposant sur l'objet appelant. Si vous avez besoin duthisdynamique, consultez la liaison de fonctions.
Si vous utilisez fréquemment la curryfication et l'application partielle, envisagez d'utiliser la bibliothèque Lodash, qui fournit des fonctions utilitaires pratiques comme _.curry et _.partial.
Combiner avec les méthodes de tableau
La curryfication peut être efficacement combinée avec les méthodes de tableau comme map, filter et reduce pour un code concis et expressif.
Explication :
- La fonction
curriedMultiplyest utilisée pour créermultiplyByTwo, une fonction qui multiplie son argument par 2. - Le tableau
numbersest transformé grâce àmap, en appliquantmultiplyByTwoà chaque élément, produisant un nouveau tableau de nombres doublés.
Conclusion
La curryfication et l'application partielle en JavaScript offrent de puissantes techniques pour simplifier la composition de fonctions, améliorer la réutilisabilité du code et en améliorer la lisibilité. La curryfication transforme les fonctions à arguments multiples en une série de fonctions unaires, permettant un code plus flexible et modulaire. L'application partielle permet de prédéfinir les arguments d'une fonction, facilitant la réutilisation du code et la simplification des fonctions complexes. En tirant parti de ces concepts de programmation fonctionnelle, les développeurs peuvent écrire du code JavaScript plus propre, plus concis et plus maintenable.