Origen

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 cálculo lambda. 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 programación funcional 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.

Sin efectos colaterales 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:

  1. Siempre obtendremos el mismo resultado dado los mismos parámetros de entrada.
  2. 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:

function cambiarNombre(nombre) {
  persona.nombre = nombre;
}
const jaime = { nombre: 'Jaime', edad: 30 };
cambiarNombre('Juan')
console.log(jaime); // { nombre: 'Juan', edad: 30 }

En la anterior función notamos que se cambia objeto jaime, 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 nombre del objeto siga siendo 'jaime'.

Ahora veamos la versión funcional de la función:

function cambiarNombre(nombre, persona) {
  return {
    nombre: nombre,
    edad: persona.edad
  }
}
const jaime = { nombre: '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 jaime, más bien crea un nuevo objeto con la misma edad que jaime y con la propiedad nombre igual a 'Juan', con esto evitamos efectos colaterales por si el objeto jaime 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 persona y nombre 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', function () {
  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.

La función 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 a 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.

  1. El caso base, el cual permite detener la ejecución de subsecuentes invocaciones de la función recursiva.
  2. 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 es 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.

es_MXES_MX