Una introducción rápida a pipe () y compose () en JavaScript

La programación funcional ha sido un viaje bastante revelador para mí. Esta publicación, y publicaciones similares, son un intento de compartir mis conocimientos y perspectivas mientras recorro nuevas tierras de programación funcional.

Ramda ha sido mi biblioteca FP de referencia debido a lo mucho más fácil que hace la programación funcional en JavaScript. Lo recomiendo altamente.

Tubo

El concepto de pipees simple: combina nfunciones. Es una tubería que fluye de izquierda a derecha, llamando a cada función con la salida de la última.

Escribamos una función que devuelva la de alguien name.

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

Escribamos una función que escriba cadenas en mayúsculas.

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

Entonces, si quisiéramos obtener y usar mayúsculas en personel nombre, podríamos hacer esto:

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

Está bien, pero eliminemos esa variable intermedia name.

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

Mejor, pero no me gusta ese anidamiento. Puede estar demasiado lleno. ¿Qué pasa si queremos agregar una función que obtenga los primeros 6 caracteres de una cadena?

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

Resultando en:

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

Vamos a volvernos locos y agregar una función para revertir cadenas.

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

Ahora tenemos:

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

Puede ser un poco ... mucho.

¡Pipe al rescate!

En lugar de bloquear funciones dentro de funciones o crear un montón de variables intermedias, ¡vamos a pipetodas las cosas!

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

Arte puro. ¡Es como una lista de tareas pendientes!

Vamos a analizarlo.

Para propósitos de demostración, usaré una pipeimplementación de uno de los artículos de programación funcional de Eric Elliott.

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

Me encanta esta pequeña frase de una sola línea.

Usando parámetros de descanso , vea mi artículo sobre eso, podemos canalizar nfunciones. ¿Cada función toma la salida de la anterior y se reduce todo ? a un solo valor.

Y puede usarlo como lo hicimos anteriormente.

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

Expandiré pipey agregaré algunas declaraciones de depuración, e iremos línea por línea.

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

Llame pipecon nuestro ejemplo y deje que las maravillas se desarrollen.

Consulte las variables locales. functionses una matriz de las 4 funciones y valuees { name: 'Buckethead' }.

Dado que usamos parámetros de descanso , pipepermite usar cualquier cantidad de funciones. Se repetirá y llamará a cada uno.

En el próximo depurador, estamos dentro reduce. Aquí es donde currentValuese pasa currentFunctiony se devuelve.

Vemos que el resultado es 'Buckethead'porque currentFunctiondevuelve la .namepropiedad de cualquier objeto. Eso se devolverá reduce, lo que significa que se convertirá en el nuevo la currentValuepróxima vez. Vayamos al siguiente depurador y veamos.

Ahora currentValuees ‘Buckethead’porque eso es lo que se devolvió la última vez. currentFunctiones uppercase, así 'BUCKETHEAD'será el próximo currentValue.

La misma idea, arranca ‘BUCKETHEAD’los primeros 6 caracteres y entrégalos a la siguiente función.

reverse(‘.aedi emaS’)

¡Y tu estas listo!

¿Qué pasa con compose ()?

Es solo pipeen la otra dirección.

Entonces, si quisieras el mismo resultado que el pipeanterior, harías lo contrario.

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

¿Observa cómo getNamees el último en la cadena y el reverseprimero?

Here’s a quick implementation of compose, again courtesy of the Magical Eric Elliott, from the same article.

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

I’ll leave expanding this function with debuggers as an exercise to you. Play around with it, use it, appreciate it. And most importantly, have fun!