Une introduction rapide à pipe () et compose () en JavaScript

La programmation fonctionnelle a été pour moi un voyage révélateur. Cet article, et les articles similaires, sont une tentative de partager mes idées et perspectives alors que je parcours de nouveaux terrains de programmation fonctionnelle.

Ramda est ma bibliothèque FP préférée en raison de la facilité avec laquelle elle facilite la programmation fonctionnelle en JavaScript. Je le recommande fortement.

Tuyau

Le concept de pipeest simple - il combine des nfonctions. C'est un tube coulant de gauche à droite, appelant chaque fonction avec la sortie de la dernière.

Écrivons une fonction qui renvoie celle de quelqu'un name.

getName = (person) => person.name; getName({ name: 'Buckethead' }); // 'Buckethead' 

Écrivons une fonction qui met les chaînes en majuscules.

uppercase = (string) => string.toUpperCase(); uppercase('Buckethead'); // 'BUCKETHEAD' 

Donc, si nous voulions obtenir et mettre en majuscule personle nom de, nous pourrions faire ceci:

name = getName({ name: 'Buckethead' }); uppercase(name); // 'BUCKETHEAD' 

C'est bien mais éliminons cette variable intermédiaire name.

uppercase(getName({ name: 'Buckethead' })); 

Mieux, mais je n'aime pas cette nidification. Il peut y avoir trop de monde. Que faire si nous voulons ajouter une fonction qui obtient les 6 premiers caractères d'une chaîne?

get6Characters = (string) => string.substring(0, 6); get6Characters('Buckethead'); // 'Bucket' 

Résultant en:

get6Characters(uppercase(getName({ name: 'Buckethead' }))); // 'BUCKET'; 

Soyons vraiment fous et ajoutons une fonction pour inverser les chaînes.

reverse = (string) => string .split('') .reverse() .join(''); reverse('Buckethead'); // 'daehtekcuB' 

Maintenant nous avons:

reverse(get6Characters(uppercase(getName({ name: 'Buckethead' })))); // 'TEKCUB' 

Cela peut devenir un peu… beaucoup.

Pipe à la rescousse!

Au lieu de brouiller les fonctions dans les fonctions ou de créer un tas de variables intermédiaires, passons à pipetout!

pipe( getName, uppercase, get6Characters, reverse )({ name: 'Buckethead' }); // 'TEKCUB' 

De l'art pur. C'est comme une liste de choses à faire!

Passons en revue.

À des fins de démonstration, j'utiliserai une pipeimplémentation de l'un des articles de programmation fonctionnelle d'Eric Elliott.

pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x); 

J'adore ce petit one-liner.

En utilisant les paramètres de repos , voir mon article à ce sujet, nous pouvons canaliser des nfonctions. Chaque fonction prend la sortie de la précédente et tout est réduit ? à une valeur unique.

Et vous pouvez l'utiliser comme nous l'avons fait ci-dessus.

pipe( getName, uppercase, get6Characters, reverse )({ name: 'Buckethead' }); // 'TEKCUB' 

Je vais développer pipeet ajouter quelques instructions de débogage, et nous irons ligne par ligne.

pipe = (...functions) => (value) => { debugger; return functions.reduce((currentValue, currentFunction) => { debugger; return currentFunction(currentValue); }, value); }; 

Appelez pipeavec notre exemple et laissez les merveilles se dérouler.

Vérifiez les variables locales. functionsest un tableau des 4 fonctions, et valueest { name: 'Buckethead' }.

Puisque nous avons utilisé des paramètres de repos , pipepermet d'utiliser n'importe quel nombre de fonctions. Il va juste boucler et appeler chacun.

Sur le prochain débogueur, nous sommes à l'intérieur reduce. C'est là où currentValueest passé currentFunctionet renvoyé.

Nous voyons que le résultat est 'Buckethead'que currentFunctionrenvoie la .namepropriété de n'importe quel objet. Cela sera retourné reduce, ce qui signifie qu'il deviendra le nouveau la currentValueprochaine fois. Frappons le prochain débogueur et voyons.

Maintenant, currentValuec'est ‘Buckethead’parce que c'est ce qui est revenu la dernière fois. currentFunctionest uppercase, ainsi 'BUCKETHEAD'sera le prochain currentValue.

La même idée, cueillez ‘BUCKETHEAD’les 6 premiers personnages et passez-les à la fonction suivante.

reverse(‘.aedi emaS’)

Et tu as fini!

Qu'en est-il de compose ()?

C'est juste pipedans l'autre sens.

Donc, si vous vouliez le même résultat que notre pipeci-dessus, vous feriez le contraire.

compose( reverse, get6Characters, uppercase, getName )({ name: 'Buckethead' }); 

Remarquez comment getNameest le dernier dans la chaîne et le reversepremier?

Voici une mise en œuvre rapide compose, encore une fois avec la permission du Magical Eric Elliott, du même article.

compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x); 

Je vais vous laisser développer cette fonction avec debuggers comme exercice. Jouez avec, utilisez-le, appréciez-le. Et surtout amusez-vous!