Los paradigmas de programación funcional en JavaScript moderno: composición de funciones

En los dos capítulos anteriores revisamos algunos de los paradigmas básicos de la programación funcional: funciones puras e inmutabilidad. Si no los ha leído, puede comenzar desde aquí .

En este artículo, ampliaremos nuestro conocimiento de las funciones y sus propiedades sentando las bases de cómo se pueden usar juntas para componer la lógica de su programa.

El objetivo de la programación funcional es construir nuestros programas utilizando principalmente pequeñas funciones puras que se pueden ensamblar juntas como si estuviéramos combinando pequeñas piezas de lego.

¿Qué es la composición de funciones?

La composición de funciones es el acto de combinar múltiples funciones juntas para crear flujos lógicos más complejos. Al usar esta técnica, podemos evitar estructuras de código iterativo que se leen como una secuencia de operaciones y, en su lugar, abstraer la complejidad del proceso combinando las instrucciones en una sola función.

No se deje intimidar por la duración de la última frase. Para decirlo simplemente, aquí comenzaremos a aprender cómo combinar funciones juntas.

Según lo que aprendimos en uno de los artículos anteriores, las funciones deben ser pequeñas, puras y no generar efectos secundarios. Una función que se adhiere a esas condiciones es una función pura y las funciones puras son bastante fáciles de componer.

Las funciones siempre devuelven el mismo tipo y, dados los mismos argumentos, devuelven el mismo valor, lo que las hace predecibles. Y a diferencia de las películas, desea que sus funciones tengan un resultado predecible para que pueda usarlas fácilmente en su código base.

La condición previa que debemos tener en cuenta es que cuando redactamos funciones, cada una debe tomar el resultado del anterior. No podemos simplemente tomar algunas funciones, unirlas y esperar que funcionen, sus necesidades de entrada y salida deben coincidir .

¡Felicidades, acabas de tener tu primera experiencia con la composición de funciones!

Puede imaginar la composición de funciones como una combinación de tuberías. La ejecución comienza desde la primera (la más a la derecha) y cualquiera que sea la salida se transmitirá a la segunda. La segunda función toma eso, hace algo con él y lo pasa a la siguiente función, y así sucesivamente.

Más adelante en el artículo encontraremos otra forma de componer funciones juntas, pero para este ejemplo en particular, recuerde que la ejecución comienza de adentro hacia afuera o de derecha a izquierda, lo que sea más fácil de recordar

Ahora volvamos a las condiciones previas que debemos tener en cuenta al usar la composición de funciones. Si tiene una función que toma un número como argumento, no puede realmente componerlo con uno que devuelva una cadena. Bueno, técnicamente puedes, pero no obtendrás el resultado deseado.

Otra forma simple de explicar la composición es que el resultado de llamar consecutivamente a las funciones debe ser igual al resultado de componerlas juntas.

Al probar que ambos enfoques tienen el mismo resultado, estamos seguros de que hemos compuesto nuestras funciones de manera adecuada y que no dependen de ningún aporte adicional.

También note que no solo estamos pasando la función interna como un argumento, esta sería una Función de Orden Superior. La función más interna en realidad se llama con sus argumentos.

Dónde se aplica?

Cada vez que estoy aprendiendo algo nuevo, me gusta considerar dónde podría aplicarlo y cómo puede ser útil.

Recientemente tuve un caso en el que una API esperaba recibir una carga JSON formateada de una manera específica. Una forma de hacerlo es implementar la lógica de formateo en el código en sí, pero esto no es algo que realmente desee. No importa qué servicio o aplicación esté creando, no quiere que el interior de su aplicación dependa de algo externo.

Entonces, decidí implementar un paso adicional antes de enviar la carga útil para analizarlo y prepararlo. El enfoque más simple aquí es crear una función parsePayload que toma el objeto y devuelve uno nuevo listo para ser enviado.

Mi preocupación era que el formato requerido de la API del cliente podría cambiar, así que decidí dividir las operaciones en múltiples funciones pequeñas y ser más flexible cuando se trata de modificaciones en el futuro. Otro gran beneficio de eso es la claridad del proceso de análisis sintáctico.

La misma sintaxis que antes pero dividida en nuevas líneas

En este ejemplo particular comenzamos desde filterEmpty . No olvides que con esta manera particular de componer la ejecución siempre es de adentro hacia afuera. Después de filtrar las propiedades vacías, devolvemos el objeto a la función formatValues que tomará este objeto y hará lo que sugiere su nombre. Devuelve el objeto formateado a mapKeys que hace algo de trabajo en las teclas y luego termina siendo codificado para que pueda ser enviado.

Si estuviéramos trabajando con una matriz, esto podría lograrse con los métodos integrados en el objeto Array de JavaScript, pero esto hace que sea más claro para los futuros desarrolladores que heredan la base de código qué es exactamente lo que estaba tratando de hacer.

Una explicación más matemática

Aunque trato de no dar explicaciones matemáticas, la composición de funciones es un concepto que se describe fácilmente con un ejemplo de matemáticas.

Imagine que tiene algún cálculo x + 3 y quiere multiplicar su resultado por 10 ; lo escribiría como (x + 3) * 10 . Así es como podemos implementar fácilmente esto usando la composición de funciones

Parece bastante intuitivo ¿no? Puede leerse en voz alta y tendrá perfecto sentido lo que está tratando de hacer: sume los números 3 y 5 , luego multiplique por 10 . Este es el ejemplo que hizo que la composición de funciones haga clic para mí y creo que es lo que mejor ilustra cómo puede ser el código descriptivo funcional.

¿Por qué es esto una cosa?

La primera vez que lee sobre esto, probablemente se pregunte por qué es necesario si puede usar algún tipo de encadenamiento. JavaScript tiene muchas funciones de utilidad para sus tipos de datos, por lo que no es difícil modificar matrices o cadenas.

Sin embargo, para encadenar al trabajo, necesita pasar el mismo objeto porque no solo está llamando a una función, sino que la está llamando al objeto que se pasó del anterior. Por lo tanto, necesita ese objeto para incluir todas las funciones que desea encadenar.

En un caso en el que desea filtrar y mapear una matriz, el encadenamiento es el enfoque mejor y más legible. Pero en un escenario en el que espera que el tipo de datos cambie o está implementando un flujo complejo que no se puede admitir con los métodos integrados, la composición puede beneficiarlo sin sacrificar la legibilidad.

Bibliotecas

Hay muchas bibliotecas JS construidas con programación funcional en mente, pero para este ejemplo usaremos Ramda. Es la elección perfecta si desea probar el agua y ver cómo se sentirá al utilizar un enfoque más funcional para construir sus programas.

Para simplificar, usemos un ejemplo con algunas operaciones computacionales.

Este ejemplo es bastante simple y solo utiliza tres funciones, por lo que es aún comprensible. Sin embargo, si necesitamos componer cinco funciones, saldrá de control y será más difícil de leer. Siempre podemos dividir las funciones en filas diferentes, pero debemos tener cuidado de no exagerar, especialmente cuando aumenta el recuento de las funciones compuestas.

Para evitar confusión innecesaria podemos utilizar de Ramda compose función. El beneficio de usar una biblioteca es que la sintaxis puede ser fácilmente comprendida y mejorada para que los colegas que no han jugado con la programación funcional puedan comprender qué está sucediendo exactamente sin depender directamente de usted.

La primera diferencia que puede ver aquí es que dentro de la llamada de redacción no proporcionamos los argumentos para la primera función. De hecho, componer devolverá una nueva función que luego podemos llamar. Además, tenga en cuenta que las funciones creadas con compuestas se ejecutan de la última a la primera, lo que significa que la sum se ejecutará primero, luego la square y luego se addTen . Si desea que se ejecuten desde el principio hasta el último, puede usar la función de pipe que proporciona Ramda. La sintaxis es 100% igual, siendo la diferencia el orden de ejecución.

¿Por qué es importante la composición de funciones?

La composición de funciones está en el núcleo de la programación funcional y le enseñará los conceptos básicos que necesitará para crear funciones y flujos potentes. Lo que hemos aprendido en este artículo apenas araña la superficie del tema, pero sirve para que se sienta más cómodo con la manipulación de funciones

¿Que sigue?

Cubrimos los aspectos básicos de la composición de funciones y probablemente debería sentirse cómodo al componer sus funciones en conjunto, ya sea usando Ramda o JavaScript simple.

Este es el momento en que el vendedor dice "Pero espera, ¡hay más!". Y seguro que hay más: al combinar lo que hemos aprendido en este artículo con algunos otros conceptos de programación funcional, currying y aplicación parcial , podemos crear abstracciones increíblemente poderosas y descubrir el verdadero potencial de las funciones.

¡En el próximo artículo veremos una aplicación parcial y qué tiene de especial!

Los paradigmas de programación funcional en el JavaScript moderno: funciones puras
JavaScript es uno de los lenguajes de programación más populares que existen. Se ejecuta en el navegador, en el escritorio, en el dispositivo móvil … hackernoon.com
Los paradigmas de programación funcional en JavaScript moderno: inmutabilidad
Este es el segundo capítulo de una serie de artículos sobre paradigmas prácticos de Programación Funcional. Si no has leído … hackernoon.com