En hurtig introduktion til pipe () og compose () i JavaScript

Funktionel programmering har været en meget øjenåbnende rejse for mig. Dette indlæg, og indlæg som det, er et forsøg på at dele min indsigt og perspektiver, når jeg trækker nye funktionelle programmeringslande.

Ramda har været mit go-to FP-bibliotek på grund af hvor meget lettere det gør funktionel programmering i JavaScript. Jeg kan varmt anbefale det.

Rør

Konceptet med pipeer simpelt - det kombinerer nfunktioner. Det er et rør, der flyder fra venstre mod højre, der kalder hver funktion med output fra den sidste.

Lad os skrive en funktion, der returnerer andres name.

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

Lad os skrive en funktion, der ophæver strenge.

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

Så hvis vi ønskede at få og kapitalisere personnavnet, kunne vi gøre dette:

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

Det er fint, men lad os fjerne den mellemliggende variabel name.

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

Bedre, men jeg er ikke glad for den indlejring. Det kan blive for overfyldt. Hvad hvis vi vil tilføje en funktion, der får de første 6 tegn i en streng?

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

Resulterende i:

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

Lad os blive virkelig skøre og tilføje en funktion til at vende strenge.

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

Nu har vi:

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

Det kan blive lidt ... meget.

Rør til undsætning!

I stedet for at fastklemme funktioner inden for funktioner eller oprette en masse mellemliggende variabler, lad os pipealle tingene!

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

Ren kunst. Det er som en todo-liste!

Lad os gå igennem det.

Til demo-formål bruger jeg en pipeimplementering fra en af ​​Eric Elliotts funktionelle programmeringsartikler.

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

Jeg elsker denne lille one-liner.

Brug resten parametre, se min artikel om, at vi kan pipe nfunktioner. Hver funktion tager output fra den foregående, og det er alt sammen reduceret ? til en enkelt værdi.

Og du kan bruge det ligesom vi gjorde ovenfor.

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

Jeg udvider pipeog tilføjer nogle fejlretningserklæringer, og vi går linje for linje.

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

Ring pipemed vores eksempel og lad vidundere udfolde sig.

Tjek de lokale variabler. functionser en matrix af de 4 funktioner, og valueer { name: 'Buckethead' }.

Da vi brugte resten parametre, pipetillader enhver række funktioner, der skal bruges. Det vil bare løkke og ringe til hver enkelt.

På den næste debugger er vi inde reduce. Det er her, currentValueder sendes til currentFunctionog returneres.

Vi ser, at resultatet er, 'Buckethead'fordi det currentFunctionreturnerer .nameejendommen til ethvert objekt. Det returneres reduce, hvilket betyder, at det bliver nyt currentValuenæste gang. Lad os ramme den næste fejlfinding og se.

Det er nu currentValue, ‘Buckethead’fordi det er, hvad der blev returneret sidste gang. currentFunctioner uppercase, så 'BUCKETHEAD'bliver det næste currentValue.

Den samme idé, pluk de ‘BUCKETHEAD’første 6 tegn og aflever dem til næste funktion.

reverse(‘.aedi emaS’)

Og du er færdig!

Hvad med compose ()?

Det er bare pipei den anden retning.

Så hvis du ønskede det samme resultat som vores pipeovenfor, ville du gøre det modsatte.

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

Bemærk hvordan getNameer sidste i kæden og reverseer først?

Her er en hurtig implementering af composeigen med tilladelse til den magiske Eric Elliott fra den samme artikel.

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

Jeg forlader at udvide denne funktion med debuggers som en øvelse for dig. Spil rundt med det, brug det, værdsat det. Og vigtigst af alt, have det sjovt!