Programación funcional: Haga que su código sea más fácil de leer
La programación funcional, se resume en que las funciones puras son más fáciles de leer y comprender. Todas las dependencias de la función están en su definición y, por lo tanto, son más fáciles de ver. Las funciones puras también tienden a ser pequeñas y hacen una cosa. No usan esto, una fuente constante de confusión.
Encadenamiento
El encadenamiento es una técnica utilizada para simplificar el código donde se aplican múltiples métodos a un objeto uno tras otro.
Veamos y comparemos los dos estilos: imperativo y funcional. En el estilo funcional, uso la caja de herramientas básica para las operaciones de lista filter()
y map()
. Luego los encadeno.
Tomé el caso de una colección de tareas. Una tarea tiene un id, una descripción (desc
), un booleano completed
, un type
y un objeto de user
asignado. El objeto de usuario tiene una propiedad de name
.
Observe las devoluciones de llamada para filter()
y map()
como funciones puras con nombres que revelan la intención.
map()
transforma una lista de valores en otra lista de valores usando una función de mapeo.
Aquí hay una prueba de rendimiento que mide la diferencia entre los dos estilos. Parece que el enfoque funcional es un 60% más lento. Cuando el proceso imperativo termina en 10 milisegundos, el enfoque funcional terminará en 16 milisegundos. En ese caso, utilizar el bucle imperativo será una optimización prematura.
Estilo sin puntos
En el ejemplo anterior, usé el estilo sin puntos al componer funciones. Point-free es una técnica que mejora la legibilidad al eliminar los argumentos innecesarios. Considere el siguiente código:
En un estilo sin puntos está escrito sin argumentos:
Para obtener más información sobre sin puntos, consulte Cómo la composición sin puntos lo convertirá en un mejor programador funcional.
Aplicación parcial
A continuación, quiero ver cómo podemos mejorar la legibilidad y también reutilizar una función existente. Antes de hacer eso, necesitamos una nueva función en nuestra caja de herramientas.
La aplicación parcial se refiere al proceso de fijar una serie de argumentos a una función.
Es una forma de pasar de la generalización a la especialización.
Para una aplicación parcial, podemos usar la función partial()
de una biblioteca popular como underscore.js o lodash.js. El método bind()
también puede realizar una aplicación parcial.
Digamos que queremos refactorizar el siguiente código imperativo a un estilo funcional y más fácil de leer:
Como dije, esta vez queremos crear una función genérica que pueda usarse para filtrar por cualquier tipo de tarea. isTaskOfType()
es la función genérica. La función partial()
se utiliza para crear una nueva función de predicado isCreateNewContent()
que filtra por un tipo específico.
Una función de predicado es una función que toma un valor como entrada y devuelve verdadero / falso en función de si el valor satisface la condición.
Observe la función de predicado. Tiene un nombre que expresa su intención. Cuando leo tasks.filter(isCreateNewContent)
, entiendo claramente qué tipo de tasks
estoy seleccionando.
filter()
selecciona valores de una lista basada en una función de predicado que decide qué valores deben mantenerse.
Reducir
Comenzaré un nuevo ejemplo usando una lista de compras. Así es como puede verse la lista:
Calcularé el precio total y el precio de las frutas únicamente. A continuación se muestra el estilo imperativo:
Adoptar el enfoque funcional en este caso requerirá el uso de reduce()
para calcular el precio total.
reduce()
reduce una lista de valores a un valor.
Como hicimos antes, creamos nuevas funciones para las devoluciones de llamada requeridas y les damos nombres reveladores de intenciones: addPrice()
y areFruits()
.
Conclusión
Las funciones puras son más fáciles de leer y razonar.
La programación funcional dividirá las operaciones de la lista en pasos como: filtrar, mapear, reducir, ordenar. Al mismo tiempo, será necesario definir nuevas funciones pequeñas puras para respaldar esas operaciones.
La combinación de la programación funcional con la práctica de dar nombres que revelen la intención mejora enormemente la legibilidad del código.