![Introducción a programación funcional](https://pensemosweb.com/wp-content/uploads/2018/04/posts_functional.programming-70-600x339-1.webp)
Introduction to functional programming
Origin
El origen de la programación funcional data del año 1936, cuando un matemático llamado Alonzo Church necesitaba resolver un problema complejo, del cual no hablaremos aquí, pero Alonzo creó una solución, el lambda calculus. De esta forma nació la programación funcional, incluso mucho antes de la primera computadora digital programable y de los primeros programas escritos en ensamblador.
La programación funcional es mucho más antigua que la programación estructurada y la programación orientada a objetos. Te recomiendo también revisar esta información sobre el paradigma de programación funcional.
Sin efectos colaterales
Todo lo que necesitas saber sobre functional programming es que “No tiene efectos colaterales“, es decir, que sus elementos (funciones) son inmutables.
Lo anterior significa que una función no se mete en lo absoluto con datos que existen fuera de ella, si puede utilizar los datos enviados como parámetros, pero no debe modificarlos, todos los datos deben ser inmutables, ¿Por qué?, pues porque así hacemos las cosas más simples y con menos errores, más adelante tenemos un ejemplo. A este comportamiento se le llama puro.
No side effects nos permite entender todas las demás características de la programación funcional, por mencionar algunas principales:
- Funciones puras
- Inmutabilidad
- Funciones de alto nivel
- Funciones currificadas
- Recursividad
- Menos errores de programación porque no realiza efectos colaterales.
Toda función teniendo ciertos parámetros, cuando se ejecuta, obtendremos un resultado basado en esos parámetros, y si volvemos a utilizar los mismos parametros, entonces la función regresara el mismo valor. Esto es debido a que una función no depende de más datos externos que puedan ser modificados, así tendremos más seguridad y nuestro código se vuelve más entendible.
Javascript es un lenguaje de programación muy potente y multiparadigma, soporta el paradigma funcional y lo utilizaremos para nuestros ejemplos.
Funciones puras e inmutabilidad
Bueno, realmente ya explicamos de manera indirecta que es una función pura en los párrafos anteriores, pero veamos una definición formal. Una función pura es la que cumple con estas dos características:
- Siempre obtendremos el mismo resultado dado los mismos parámetros de entrada.
- No tiene ningún efecto colateral observable, no modifica el estado de los datos externos, por lo que todos los datos deben ser inmutables.
Un ejemplo de como cambiar el nombre de una objeto persona usando primero una función con efector colaterales:
functions cambiarNombre(name) {
person.name = name;
}
const James = { name: 'Jaime', edad: 30 };
cambiarNombre('Juan')
console.log(jaime); // { nombre: 'Juan', edad: 30 }
En la anterior función notamos que se cambia objeto James
, el cual supongamos que es parte del estado de nuestra aplicación, pero puede haber otras funciones que utilicen este objeto, por lo que se puede generar problemas si alguien más espera que la propiedad name
del objeto siga siendo 'jaime'
.
Ahora veamos la versión funcional de la función:
functions cambiarNombre(name, person) {
return {
name: name,
edad: person.edad
}
}
const James = { name: 'Jaime', edad: 30 };
const juan = cambiarNombre('Juan', jaime);
console.log(jaime); // { nombre: 'Jaime', edad: 30 }
console.log(juan); // { nombre: 'Juan', edad: 30 }
En la versión funcional, cambiarNombre
no modifica el objeto James
, más bien crea un nuevo objeto con la misma edad que James
y con la propiedad name
equal to 'Juan'
, con esto evitamos efectos colaterales por si el objeto James
es utilizado por otra función u otro programador.
Con esta función sin efecto colateral, nos damos cuenta de que los datos se manejan sin ser modificados, es decir, inmutables, los parámetros person
and name
nunca fueron cambiados.
Funciones de alto nivel y currificadas
Una función de alto nivel es una función que implementa al menos una de las opciones siguientes:
- Recibir como parámetro una o más funciones
- Regresar como resultado una función
Un ejemplo muy común usado en nuestro navegador web es agregar escuchadores de eventos:
<button id="boton">Soy un botón</button>
const boton = document.querySelector('#boton');
boton.addEventListener('click', functions () {
alert('Click sobre el boton');
});
En el código pasamos como segundo parámetro del método addEventListener
una función anónima (sin nombre) que mostrará un alerta al dar click
sobre un botón con id
igual a ‘botón’. Dado que pasamos por parámetro una función, entonces se dice que es una función de alto nivel.
Otro ejemplo de función de alto nivel lo podemos observar en los métodos de arreglos en Javascript, el código de abajo toma un arreglo de números y crea otro arreglo con los valores aumentados al doble.
See the Pen Funciones de alto nivel by Jaime Cervantes Velasco (@jaime_cervantes_ve) on CodePen.
The function map
retorna un nuevo arreglo, no cambia el arreglo original
, por lo que decimos que no existe un efecto colateral.
Las funciones currificadas son funciones de alto nivel que como resultado regresan otra función, de tal manera que el acto de currificar es convertir una función de más de un parámetro en dos o más funciones que reciben parcialmente esos parámetros en dos o más invocaciones currificadas.
See the Pen Funciones de alto nivel y funciones currificadas by Jaime Cervantes Velasco (@jaime_cervantes_ve) on CodePen.
Las funciones flecha o arrow functions nos permiten acceder al contexto de los parámetros, es por eso que seguimos teniendo acceso al parámetro to
de la primera invocación de summaryCurry
. De esta manera cuando se les define un valor, una arrow function
puede ver ese valor. Para lograr lo mismo sin funciones flecha se debe utilizar la variable arguments
que existe como variable local dentro de todas las funciones en JavaScript. Para mantener las cosas simples, de momento no veremos como hacerlo con arguments
.
Función recursiva
Para crear una función recursiva, primero se define un caso base, luego a través de la división del problema en pedazos más pequeños se encuentra un patrón, este patrón llamado caso recursivo se repite muchas veces, es aquí donde la función se llama así misma y acumula los resultados, la ejecución se detiene hasta llegar a su caso base.
- El caso base, el cual permite detener la ejecución de subsecuentes invocaciones de la función recursiva.
- El caso recursivo, el cual permite que una función se llame a sí misma hasta llegar al caso base.
El factorial de un número positivo es la multiplicación de ese número por el número inmediato menor y así sucesivamente hasta llegar al número 1
, su notación es n!
, donde n
es un número positivo. Por ejemplo el factorial de 5
is 120
, 5! = 5 x 4 x 3 x 2 x 1 = 120
.
// caso base:
1! = 1 = 1
// caso recursivo, ejemplos:
2! = 2 x 1 = 2 x 1!
3! = 3 x 2 x 1 = 3 x 2!
4! = 4 x 3 x 2 x 1 = 4 x 3!
5! = 5 x 4 x 3 x 2 x 1 = 5 x 4!
Con estos datos, podemos crear nuestra formula factorial(n) = n * factorial(n-1)
, lo cual seria nuestro caso recursivo, pero debemos de añadir nuestro caso base para que se detenga, cuando n=1
debemos obtener como resultado 1
.
Veamos como quedaría nuestra función recursiva en javascript:
See the Pen Funciones recursivas by Jaime Cervantes Velasco (@jaime_cervantes_ve) on CodePen.
Con esto cubrimos las cualidades principales de la programación funcional, pero recuerda, lo más importante es que NO existen efectos colaterales.