Introducción a programación funcional en JavaScript — Parte 1

Continuamos con el curso de JavaScript. Esta semana estamos viendo estructuras de datos, … y me veo tentado a hacer un pequeño inciso para introducir algunos conceptos de programación funcional, ya que las arrays se prestan como ejemplos y Array.prototype incluye métodos como map(), reduce() o filter() como alternativas “funcionales” a la iteración con bucles.

Qué es la programación funcional?

Antes de entrar en detalles, no está de más aclarar que la Programación Funcional (Functional Programming o FP) es un “paradigma de programación”. Esto quiere decir que es una forma de pensar en cómo se organiza un programa basado en una serie de principios (que mencionaremos más abajo). Cómo comparación, los paradigmas predominantes son la Programación Orientada a Objetos (OOP — Object Oriented Programming) y Programación por Procedimientos (Procedural Programming).

Functional programming is a programming paradigm, meaning that it is a way of thinking about software construction based on some fundamental, defining principles […]
Fuente: https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0#.xx2uha5ki

Declarativo vs Imperativo

A diferencia de la programación por procedimientos y OOP, que son paradigmas “imperativos”, la programación funcional es “declarativa”.

Esto básicamente quiere decir que en estilo functional expresamos nuestra lógica sin describir el “control de flujo” (bucles, condicionales, …), enfocándonos en “qué” estamos haciendo, en vez de “cómo” se está haciendo, que sería el enfoque imperativo.

Imaginemos que tenemos que implementar una función que recibe dos arrays (a y b) y debe retornar un nuevo arreglo con los elementos de a que también existen en b, tantas veces como aparezcan en a (una pequeña variación de un ejercicio reciente). Además el resultado debe de estar ordenado.

Vamos a ver 4 implementaciones diferentes, empezando por la imperativa, y poco a poco vamos a ir abstrayendo la parte del “control de flujo” y delegando esta responsabilidad a “funciones”.

La primera implementación va a ser completamente imperativa, basada en dos bucles. Un bucle for para iterar sobre la primera array, y un segundo for anidado donde podemos ver si el elemento actual de a existe en b. Si encontramos el elemento de a en b podemos “romper” el bucle interior, ya que sólo nos interesa saber cuantas veces aparece en a, y si aparece o no en b (no cuántas veces aparece en b).

Listo, una solución clara, que podemos leer de arriba a abajo y de izquierda a derecha, en el más puro estilo imperativo. Podríamos decir que, desde el punto de vista de JavaScript, nuestro código tiene un nivel bajo de abstracción, y de hecho la única abstracción sería el uso de push() en vez de usar una asignación, y el sort() del final, por supuesto.

Igual que hemos usado push() y sort(), podemos usar otros métodos que JavaScript nos ofrece para manipular arrays. Para empezar con nuestra tarea de abstracción, vamos a reemplazar el bucle interior por el método indexOf(), que nos permite comprobar si el elemento actual de a (determinado por el primer bucle) existe en b: la misma lógica que antes. Nuestra nueva implementación luciría así:

Ahora sólo tenemos un bucle, y la segunda iteración está “escondida” o “abstraída” detrás del método indexOf(). De esta misma forma, también podemos reemplazar el primer bucle. Para ello vamos a usar filter(), que crea un arreglo nuevo usando los elementos de otro arreglo que cumplan una condición. A nivel lógico eso es lo que hacía nuestro bucle. Veamos la nueva implementación:

Esta nueva implementación diríamos que es “funcional”, porque delega el “control de flujo” a funciones, filter() e indexOf(). Esta implementación se limita a expresar la lógica, sin tener que ocuparse de los detalles de implementación de los bucles, sus contadores, condiciones, …

Para terminar nuestro proceso de abstracción, cabe mencionar que JavaScript, como toda tecnología, siempre está evolucionando, y hay una tendencia muy fuerte hacia la programación funcional, lo cual ha resultado en la adición de varios “features” al lenguaje en los últimos años que reflejan esta tendencia.

Haciendo uso de sintaxis moderna, disponible en las versiones más modernas de JavaScript, nuestra cuarta y última implementación quedaría así:

Podemos ver como en nuestro viaje de abstracción empezamos con dos bucles for y más de 10 líneas, y terminamos con filter() e indexOf() haciendo todo el trabajo en una sola línea. Hemos pasado de un nivel de abstracción bajo a uno mucho más alto, donde delegamos el “trabajo sucio” y como resultado nuestro código es mucho más corto y expresivo. Esto, según aumente el tamaño y la complejidad de un proyecto, puede ser muy valioso.

Qué ventajas ofrece?

  • Cómo hemos visto en el ejemplo de arriba, el código funcional tiende a ser más conciso y expresivo
  • Más predecible. Más adelante veremos que como resultado de los principios del paradigma (uso de funciones puras, inmutabilidad, evitar estado compartido y efectos secundarios, …) nuestro código será más fácil de predecir, aislar y probar.
  • Se presta a la paralelización y la computación distribuida
  • Se presta a los paradigmas asincrónicos y dirigidos por eventos
  • JavaScript, como lenguaje, tiene una naturaleza más funcional que imperativa.

Desventajas

  • Puede ser un poco desconcertante a priori.
  • Puede requerir más recursos al estar menos orientado a la máquina y más al humano.
  • Es menos común, y por ende hay menos información…
  • … no se me ocurre nada más.

Principios

No puedo terminar sin por lo menos mencionar los principios de la programación funcional, aunque sea sin entrar en detalles. Eso lo dejaremos para el próximo “post”.

  • Uso de Funciones Puras (dados los mismos inputs siempre retorna lo mismo, y sin efectos secundarios)
  • Composición de Funciones
  • Evitar el estado compartido
  • Evitar estado cambiante (inmutabilidad)

Resumen

  • La programación funcional es un paradigma de programación, como OOP y programación procedural.
  • La programación funcional es declarativa, lo contrario de imperativa.
  • Como paradigma declarativo, la programación funcional delega el control de flujo a funciones.
  • La programación funcional tiende a resultar en código más conciso y expresivo.
  • La programación funcional se presta para expresar y resolver problemas actuales de paralelización, computación distribuida, asincronía, eventos, …
  • Puede verse un poco extraño al principio, y puede requerir más recursos que implementaciones imperativas.

Lecturas complementarias

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.