Una introducción a la programación funcional con Javascript

Tus primeros pasos con la programación funcional en Javascript

Image for post
Image for post

Si no habéis estado hibernando estos últimos años muy probablemente os hayáis encontrado de una forma u otra con el término programación funcional. ¿Pero qué significa exactamente? En este artículo intentaré daros una aproximación a los principales conceptos que implica este paradigma de programación y la forma en qué podéis comenzar a usarlo si os termina por resultar interesante. Para los que como yo llevéis años trabajando bajo el enfoque de la programación orientada a objetos, la programación funcional aporta una nueva forma de enfocar y resolver diversos problemas, por lo que no es de extrañar que su uso se haya popularizado en los últimos tiempos, especialmente con la llegada de librerías como Redux o las últimas versiones de Javascript. Así que vamos allá.

Conceptos básicos

En el caso de que os haya dado por investigar ya que es eso de la programación funcional, seguramente os suenen conceptos como inmutabilidad, pure functions, las funciones reduce, map o filter o determinismo. Sin embargo, si tuviéramos que resumir en pocas palabras qué es la programación funcional podríamos hacerlo diciendo que se caracterirza por la ausencia de efectos colaterales, es decir, escribir código funcional es escribir código que no depende de datos externos de la función y que no modifica los datos que proceden de fuera de la misma.

Siguiendo con esta idea, la siguiente función:

var a = 1;function foo() {  return a + 1;}

No seguiría los principios de la programación funcional ya que depende de la variable a que procede de fuera de la función. En este caso, la versión correcta sería:

function foo(a) {  return a + 1;}

Por tanto, y como decía al principio, la programación funcional se basa en el concepto clave de que cada función solo accede a los valores pasados como parámetros de la función sin modificarlos en ningún momento. Lo cual nos lleva al siguiente punto.

Funciones puras

En base a lo que comentaba en la sección anterior, otro concepto relevante es el que se conoce como funciones puras, las cuales podemos definirlas como funciones que para los mismos datos de entrada siempre devolverán los mismos datos de salida. Siempre. Por tanto:

function a(name) {
var d = new Date();
return name + “ : la hora actual es” + d.getHour();
}

No es una función pura, ya que cada vez que la ejecutemos devolverá un texto diferente en función de la hora que sea.

Filter, map y reduce

Siguiendo con esta misma idea, la programación funcional nos pide que no iteremos sobre los arrays mediante bucles, sino que, por el contrario, empleemos las funciones filter, map y reduce. Sobre ellas hay miles de artículos en Internet por lo que daré una explicación breve sobre cada una de ellas.

Filter

Filter nos permite (oh, sorpresa!) filtrar un array en base a la condición que queramos. Por tanto, si quisiéramos filtrar un array de números para quedarnos tan solo con los superiores a 5 escribiríamos:

var a = [ 3, 5, 7, 2, 5];
var filteredA = a.filter(x => x > 5)
a // 3, 5, 7, 2, 5filteredA // 7

Como veis, a la función filter le pasamos una función cuyo primer argumento representa cada uno de los elementos del array y que devolverá un booleano en función de si se cumple o no la condición de filtrado.

Otro aspecto importante es que esta función, como las dos siguientes, crean una nueva copia del array, por lo que el array de partida permanece inalterado ( y cumpliendo por tanto los principios de la programación funcional).

Map

La función map lo que hace es crear una nuevo array vacío y ejecutar la función pasada por argumentos en cada uno de los elementos del array, añadiendo el resultado al nuevo array.

Explicado con un ejemplo, si queremos elevar al cuadrado todos los elementos de un array escribiríamos lo siguiente:

var a = [ 2, 3, 4];
var quadraticA = a.map(x => x*x);
a // 2, 3, 4quadraticA // 4, 9, 16

Reduce

Finalmente, la función que inspiró la popular librería Redux (los reducers realmente son funciones que pasaríamos en la ejecución de reduce) nos permite combinar los elementos de un array en un solo elemento aplicándoles la función pasada como argumento. Es decir, si quisiéramos obtener la suma total de los elementos de un array podríamos escribir:

var a = [1, 2, 3, 4, 5];var reducedA = a.reduce((x, ac) => x + ac);a // 1, 2, 3, 4, 5reducedA // 15

La función que pasamos a reduce consta de dos argumentos principales. El primero xes el elemento sobre el que se encuentra la iteración actual y el segundo aces el resultado de la llamada anterior. En el caso de que no proporcionemos un valor de inicio, en la primera iteración xcorresponderá con el segundo elemento y accon el primero.

Os dejo la mejor explicación gráfica que he encontrado para resumir el uso de estas funciones:

Image for post
Image for post
Anjana Sofia Vakil

Buenísima, ¿verdad?

Inmutabilidad

Otra de las ideas claves a la hora de trabajar bajo programación funcional es la inmutabilidad. De ahí que se recomiende el uso de las funciones anteriores, ya que las 3 crean copias del array sobre el que se ejecutan, de modo que éste permanece inalterado.

Es decir, en programación funcional nunca modificaremos los datos que fueron pasados mediante parámetros, sino que crearemos copias de los mismos en el caso de que necesitemos introducir variaciones en ellos.

Esto, sin embargo, puede afectar drásticamente al rendimiento de nuestras aplicaciones, puesto que realizar copias indiscriminadas de nuestros arrays puede implicar el desperdicio de gran cantidad de memoria, algo contra lo que debemos estar prevenidos. Por suerte, existen diversas soluciones que nos proveen de un nuevo tipo de datos para representar arrays (u otro tipo de colecciones) inmutables sin que por ello tengamos que renunciar al rendimiento.

La idea subyacente a estas soluciones es emplear árboles para representar las colecciones:

Image for post
Image for post
https://practicalli.github.io/clojure-webapps/persistent-data-structures/

De modo que, cuando modificamos la estructura (por ejemplo, añadiendo un nuevo elemento) se crea un nuevo árbol que comparte con el anterior los datos inalterados, reduciendo así el uso de memoria.

Actualmente para Javascript podemos emplear las librerías Mori o InmutableJs (ésta última de Facebook) para añadir inmutabilidad a nuestras colecciones.

Escritura declarativa vs escritura imperativa

Finalmente, otro de los conceptos sobre los que se asienta la programación funcional es la división de nuestro código en funciones que describan por sí mismas lo que hacen. Dicho de otro modo, es necesario tender a escribir funciones cuyo propio nombre resuma lo que hacen sin incluir en ellas ningún tipo de funcionalidad extra.

Esto servirá para crear programas mucho más inteligibles y fáciles de leer y hará nuestro código más mantenible puesto que en el momento que algo falle podremos identificar mucho más rápido el problema (recordar que estaríamos trabajando con datos inmutables, por lo que identificar el momento en que un valor es erróneo debería ser mucho más sencillo).

¿Y ahora qué?

Llegados a este punto ya conoceréis los principios básicos de la programación funcional. Si queréis adrentaros más en ella, mi consejo sería que comenzaseis a reemplazar todos los bucles por las funciones map, reduce y filter y que poco a poco dividieseis el código de vuestras funciones más grandes en funciones puras más pequeñas, de modo que os vayáis familiarizando con esta nueva forma de pensar, que al final es de lo que se trata.

¿Quieres recibir más artículos como este?

Suscríbete a nuestra newsletter:

Written by

Entre paseo y paseo con Simba desarrollo en Symfony y React

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store