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 pipe
est simple - il combine des n
fonctions. 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 person
le 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 à pipe
tout!
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 pipe
implé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 n
fonctions. 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 pipe
et 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 pipe
avec notre exemple et laissez les merveilles se dérouler.
Vérifiez les variables locales. functions
est un tableau des 4 fonctions, et value
est { name: 'Buckethead' }
.
Puisque nous avons utilisé des paramètres de repos , pipe
permet 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ù currentValue
est passé currentFunction
et renvoyé.
Nous voyons que le résultat est 'Buckethead'
que currentFunction
renvoie la .name
propriété de n'importe quel objet. Cela sera retourné reduce
, ce qui signifie qu'il deviendra le nouveau la currentValue
prochaine fois. Frappons le prochain débogueur et voyons.
Maintenant, currentValue
c'est ‘Buckethead’
parce que c'est ce qui est revenu la dernière fois. currentFunction
est 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 pipe
dans l'autre sens.
Donc, si vous vouliez le même résultat que notre pipe
ci-dessus, vous feriez le contraire.
compose( reverse, get6Characters, uppercase, getName )({ name: 'Buckethead' });
Remarquez comment getName
est le dernier dans la chaîne et le reverse
premier?
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 debugger
s comme exercice. Jouez avec, utilisez-le, appréciez-le. Et surtout amusez-vous!