En rask introduksjon til pipe () og compose () i JavaScript

Funksjonell programmering har vært en ganske øyeblikkelig reise for meg. Dette innlegget, og innlegg som det, er et forsøk på å dele min innsikt og perspektiver når jeg trekker nye funksjonelle programmeringsland.

Ramda har vært mitt gå til FP-bibliotek på grunn av hvor mye enklere det gjør funksjonell programmering i JavaScript. Jeg anbefaler det på det sterkeste.

Rør

Konseptet med pipeer enkelt - det kombinerer nfunksjoner. Det er et rør som flyter fra venstre mot høyre, og kaller hver funksjon med utgangen fra den siste.

La oss skrive en funksjon som returnerer andres name.

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

La oss skrive en funksjon som verserer strenger.

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

Så hvis vi ønsket å hente og kapitalisere personnavnet, kunne vi gjøre dette:

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

Det er greit, men la oss eliminere den mellomliggende variabelen name.

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

Bedre, men jeg er ikke glad i det hekkingen. Det kan bli for overfylt. Hva om vi vil legge til en funksjon som får de første 6 tegnene i en streng?

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

Resulterer i:

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

La oss bli veldig sprø og legge til en funksjon for å snu strengene.

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

Nå har vi:

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

Det kan bli litt ... mye.

Rør til unnsetning!

I stedet for å fastkjør funksjoner i funksjoner eller lage en mengde mellomvariabler, la oss pipealle tingene!

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

Ren kunst. Det er som en todo-liste!

La oss gå gjennom det.

For demoformål vil jeg bruke en pipeimplementering fra en av Eric Elliotts funksjonelle programmeringsartikler.

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

Jeg elsker denne lille enlinjeren.

Ved hjelp av resten parametere, se min artikkel om at vi kan rør nfunksjoner. Hver funksjon tar utgangen fra den forrige, og er alt redusert ? til en enkelt verdi.

Og du kan bruke den akkurat som vi gjorde ovenfor.

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

Jeg utvider pipeog legger til noen feilsøkingsuttalelser, og vi går linje for linje.

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

Ring pipemed vårt eksempel og la underverkene utfolde seg.

Sjekk ut de lokale variablene. functionser en matrise av de fire funksjonene, og valueer { name: 'Buckethead' }.

Siden vi brukte resten parametre, pipekan en rekke funksjoner som skal brukes. Det vil bare løkke og ringe hver enkelt.

På neste feilsøkingsprogram er vi inne reduce. Dette er hvor currentValueblir sendt til currentFunctionog returnert.

Vi ser at resultatet er 'Buckethead'fordi currentFunctionreturnerer .nameeiendommen til ethvert objekt. Det vil bli returnert reduce, noe som betyr at det blir nytt currentValueneste gang. La oss slå på neste feilsøkingsprogram og se.

currentValueer ‘Buckethead’fordi det er det som ble returnert sist gang. currentFunctioner uppercase, så 'BUCKETHEAD'blir det neste currentValue.

Den samme ideen, plukk ‘BUCKETHEAD’de første 6 tegnene og gi dem videre til neste funksjon.

reverse(‘.aedi emaS’)

Og du er ferdig!

Hva med å komponere ()?

Det er bare pipei den andre retningen.

Så hvis du ønsket det samme resultatet som pipeovenfor, ville du gjøre det motsatte.

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

Legg merke til hvordan getNameer sist i kjeden og reverseer først?

Her er en rask implementering av compose, igjen med tillatelse fra Magical Eric Elliott, fra samme artikkel.

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

Jeg lar deg utvide denne funksjonen med debuggers som en øvelse for deg. Lek med det, bruk det, setter pris på det. Og viktigst av alt, ha det gøy!