La programación funcional es un paradigma bastante popular de los últimos años, ya que permite la construcción de programas generalmente más cortos, en cuanto a cantidad de código, como también más fáciles de testear, por eso en este articulo estaremos conociendo distintas técnicas de programación funcional.
¿Que es una función?
Una función en el mundo de la matemática se puede definir como la relación que existe entre dos números, un valor de entrada y un valor de salida, imaginemos que tenemos la siguiente función:
Esta función simplemente recibe un parámetro x
y retorna el valor resultante de multiplicar ese valor x
por dos y después sumar uno, si por ejemplo esta función le pasamos dos como valor de x
obtendremos lo siguiente:
Al pasar dos como parámetro, obtenemos como valor de retorno un 5, y aquí es donde vemos claramente el concepto de función, es la relación que existe entre un valor de entrada (2) y un valor de salida (5).
Ahora en el mundo de la programación el concepto es básicamente el mismo
Si ejecutamos esta función pasándole como parámetro el dos obtendremos un 5, al igual que en nuestra función inicial, esto teniendo en cuenta que nuestra función sea matemáticamente pura lo cual significa, que siempre que se le pase el mismo valor de entrada como parámetro debe retornar el mismo valor de salida.
Closures
Los closures en el mundo de la programación es la capacidad que tiene una función de recordar el contexto en el que esta fue creada, por ejemplo imaginen el siguiente fragmento de código:
En este ejemplo la función doSomething()
retorna el valor de multiplicar myVariable
por 2, al ejecutar esta función en la linea numero 8 obtenemos como resultado el valor de 10, pero ¿Por qué al ejecutar doSomething()
se obtuvo este resultado? dado que myVariable
existe en el mismo contexto en el que doSomething()
fue creado, esta función al ser ejecutada es capaz de recordar ese contexto y hacer uso de él si es necesario.
Observemos el siguiente ejemplo:
La función doSomethingElse()
internamente crea innerVariable
y la función innerFunction()
y retorna esta función al ejecutar doSomethingElse()
en la linea 12 esta retorna la función innerFunction()
la cual ejecutamos en la linea 13, ahora, esto nos retorna 4 dado que innerFunction()
que al fin y al cabo es la función que estamos retornando de doSomethingElse()
retorna el valor de innerVariable
el cual es 4, aquí vemos de nuevo un closure, obtuvimos 4 en la linea 13 por que innerFunction()
al momento de ejecutarse recuerda el contexto en el que fue creada(doSomethingElse()
).
Higher order functions
Una higher order function es una función común y corriente que cumpla cualquiera de las siguientes dos condiciones:
- Que reciba una función como parámetro.
- Que retorne una función.
Ambas funciones son higher order functions dado que ambas cumplen con algunas de las dos condiciones anteriormente descritas.
Currying
El currying es una técnica que permite a una función recibir sus parámetros de entrada uno a la vez, si espera recibir dos parámetros de entrada al pasarle el primero, esta función nos retornara una nueva función que esperara recibir el siguiente parámetro, miremos el siguiente ejemplo:
En este tenemos la función plus()
que espera recibir los parámetros a
y b
, en las lineas 3 y 4 estamos ejecutando esta función de la forma tradicional pasando ambos parámetros al mismo tiempo y sumándolos, en la linea numero 7 lo hacemos utilizando la función curry()
(Cabe anotar que esta función no existe en javascript que es con el lenguaje que estamos haciendo el ejemplo sino que nos la provee librerías lo cual veremos más adelante), utilizar curry nos permite hacer uso de las características anteriormente descritas, al ejecutar plus()
pasándole un solo parámetro no obtenemos el resultado final, sino que se nos devuelve una nueva función que espera recibir el siguiente parámetro, y esta función al mantener referencia al parámetro anterior y al recién pasado nos retorna el valor esperado.
Esta técnica tiene principalmente dos ventajas:
- Nos permite ir de implementaciones genéricas a funciones mucho más específicas, así como en el ejemplo anterior en la linea 12 ejecutamos
plus(1)
esta nos devolvió una nueva función que siempre que le pasemos algún numero recibiremos como valor de retorno ese valor más uno, es decir que fuimos de algo genérico(Sumar dos números) a algo más específico(Sumar 1 a cualquier numero) - Hace la composición de funciones más fácil.
Composición de funciones
La composición de funciones nos permite tomar una serie de funciones y crear una nueva función a partir de ella, para eso haremos uso de la utilidad pipe()
la cual es una función que al igual que curry()
nos la ofrecerá alguna librería para poder realizar la composición:
En este ejemplo estamos creando tres funciones las cuales son addOne()
, multiplyByThree()
y addTwo()
, despues estamos creando composedFunction()
la cual es una función creada a partir de componer con pipe()
las tres funciones anteriores, cuando en la linea 11 a composedFunction()
le pasamos el valor 3 este sera pasado como argumento de la función addOne()
que es la primera función en el pipe, el resultado de esta función addOne()
sera pasado como el valor de entrada de la siguiente función del pipe multiplyByThree()
y asi hasta finalizar con todas las funciones.
Y ¿Qué pasa si las funciones tienen más de un parámetro? ahí es donde el currying nos ayuda en la composición.
En este ejemplo estamos creando la función map()
y filter()
con la utilidad curry()
donde en ambos recibiremos la función que operara sobre las funciones map()
y filter()
que existen dentro de los array de javascript en este caso.
Después al igual que en el ejemplo anterior usamos pipe()
para realizar nuestra composición y como map()
y filter()
estan creadas usando curry()
podemos pasarle por adelantado la función que operara sobre el array y estas funciones nos retornaran la siguiente función que nos estara esperando el array, al ejecutar getGenerousUsers()
pasándole un array de usuarios obtendremos de cada uno de los usuarios su propiedad sales
y despues multiplicaremos cada sales
por 3500 y al final filtraremos solo los usuarios que hayan dado ventas por mas de 300000
, como podemos ver map()
y filter()
son higher order functions ya que reciben una función como parámetro y a su vez gracias a curry()
recuerda esta funcion(closure) y retorna la siguiente función que estará esperando el array para operar sobre el.
Librerías
Para programación funcional en el mundo de javascript te recomiendo las siguientes librerías:
Conclusiones
Si tienes en cuenta los conceptos tratados en este artículo de seguro lograras una programación funcional, ya que el objetivo de esta es reducir la cantidad de líneas de código y facilitar el testeo.
Espero que te haya gustado el artículo, y si quieres ir al siguiente nivel te invito a ver nuestra especialidad de 👉 programación.