Empezamos nueva serie donde explicaremos los conceptos básicos de la programación funcional orientados al mundo JavaScript.
Empecemos por lo básico, ¿qué es la programación funcional? Citando directamente a la wikipedia

En informática, la programación funcional es un paradigma de programación declarativa basado en el uso de verdaderas funciones matemáticas. En este estilo de programación las funciones son ciudadanas de primera clase, porque sus expresiones pueden ser asignadas a variables como se haría con cualquier otro valor; además de que pueden crearse funciones de orden superior.

Digamos que es un parádigma donde todo esta basado en algún tipo de función o teoría matemática (su centro fué el cálculo lambda) y nos obliga a realizar abstracciones que no son habituales en paradigmas más habituales como la programación orientada a objetos, pero esto no implica que tengamos que ser licenciados en matemáticas para porder usarlo y beneficiarnos de sus ventajas.

Las carácteristicas y ventajas que se suelen comentar sobre este paradigma:

  • En lugar de centrarse en el ¿cómo? se centra en el ¿qué? queremos hacer, lo que hace que nos abstraigamos un poco más del problema que queremos resolver.
  • Como su nombre indica está básado en el trabajo con funciones, practicamente todo es una función, y se tiende a la realización de funciones con un trabajo mínimo y específico, por lo que tendremos funciones pequeñas donde por ejemplo si estamos trabajando en un carrito de la compra existirian métodos como sumar precio o añadir descuento, y estos métodos devolverían siempre lo mismo siempre y cuando sus valores de entrada fueran los mismos. Muchos se preguntaran pero con esto ¿cómo contruimos toda la lógica de un carrito de la compra?... esto se basaría en la composición de funciones, es decir, cogemos esas funciones pequeñas que hemos creado y componemos una lógica que cumpla con nuestras necesidades. Siguiendo con el ejemplo del carrito si se añade algo al carrito de la compra tendriamos un método añadir producto que lo que podría hacer sería incluir producto al listado, sumar precio y aplicar descuento. Esto ya lo veremos más adelante.
  • Usa lo que se conoce como funciones puras, estás funciones se llaman así porque no tienen efectos colaterales, es decir, no cambián ni hacen nada fuera del propio método (incluso un console.log es considerado un efecto colateral), por eso siempre obtenemos el mismo resultado pasandole los mismos parámetros.
  • Al basarse en las funciones puras es un lenguaje muy apto para el paralelismo, ya que no modifica no accede a valores externos no tendremos problemas de race condition o casi cualquier otro que pudiera venir surgido de la concurrencia.
  • Las funciones son ciudadanos de primera clase, son funciones de orden superior. Ambos casos están relacionados, las funciones de orden superior se las llama asi cuando son funciones que actuan directamente sobre otras funciones y entedemos funciones como ciudadanos de primera clase cuando las funciones pueden ser tratadas como cualquier otro elemento del lenguaje, es decir se pueden pasar como parámetros o se pueden devolver funciones directamente como resultado.
  • Hace uso de lo que se conoce como recursividad, es decir, una función se puede llamar a si misma, por ejemplo para recorrer un árbol de directorios, podemos tener un método que sea abrir carpeta y según vaya recorriendo si el fichero en cuestión no es una carpeta lo devuelve y si es una carpeta se llama de nuevo a abrir carpeta y así hasta que se termina el arbol.
  • Todo es inmutable, es decir, no se modifica nada existente se crean elementos nuevos dentro de las propias funciones con los cambios necesarios.
  • Suele ser un código más expresivo
  • Es un código más facilmente testeable
  • Se tiende menos a la repetición de código, ya que todo está planteado como mini funciones con un trabajo en concreto, solo tenemos que reutilizarlas cuando toque o componer funciones que las usen.

Si me pongo podría seguir escribiendo características pero bueno en general creo que nos hacemos una idea de lo que os estoy vendiendo aquí y por mucho que siga hasta que no se vean ejemplos concretos no entenderemos lo que es esto realmente por lo que vamos a empezar hablando de Javascript e Inmutabilidad

Javascript funcional

Primero hablemos un poco del caso concreto de Javascript como lenguaje funcional. Para mi Javascript es de los mejores lenguajes que existen por su versatilidad y sus minimas obligaciones, es decir, podemos hacer lo que nos de la gana. Esto en general suele crear incovenientes porque no tenemos normas que seguir como puede ser establecer un tipo a las variables, como ya sabemos una variable ahora puede ser un número y más tarde convertirse en un string sin ni siquiera hacer un casting de tipos. Esta libertad viene de la mano de multitud de posibles errores si nos descuidamos con estas cosas (que no suelen pasar en otros lenguajes).
Pero gracias a esto podemos programar como más nos convenga o nos guste y combinar soluciones de distintos tipos para nuestro beneficio.
Como sabemos en Javascript practicamente todo es un tipo Object o parte de un tipo Object, incluso las funciones, esto nos permite usar las funciones como cualquier otro objeto: asignarlo a variables, pasarlas por parámetro, devolver funciones como resultado.... como hemos dicho al principio esto es lo básico para que un lenguaje sea funcional por lo que ya tenemos esta posibilidad, veamos como podemos cumplir con las carácteristicas básicas de un lenguaje funcional: Inmutabilidad, Funciones puras, Funciones de orden superior, Uso de currificación y composición de funciones

Inmutabilidad

Veamos lo primero el problema en cuestión

const oldNinjas = ['Ninja1', 'Ninja2']
const newNinja = 'Ninja3'

const totalNinjas = oldNinjas
totalNinjas.push(newNinja)

console.log(oldNinjas) <-- Result: [ "Ninja1", "Ninja2", "Ninja3" ]

Como vemos se ha modificado oldNinjas también, esto es funcionamiento interno de Javascript, digamos que lo que tenemos dentro de totalNinjas es una referencia en memoria a oldNinjas por eso hagamos lo que hagamos con totalNinjas se verá reproducido en oldNinjas, ojo que es un ejemplo un poco forzado para ver el caso concreto, con un oldNinjas.concat no tendriamos este problema.

Suponiendo que tenemos este problema tenemos varias opciones para resolverlo, la primera hacemos un freeze sobre oldNinjas para que no se pueda modificar hagamos lo que hagamos (nos daria error) y a continuación realizamos una copia del array:

const oldNinjas = Object.freeze(['Ninja1', 'Ninja2'])
const newNinja = 'Ninja3'

const totalNinjas = [...oldNinjas] // Copiamos
totalNinjas.push(newNinja)

console.log(totalNinjas) // result --> [ "Ninja1", "Ninja2", "Ninja3"]
console.log(oldNinjas) // result --> [ "Ninja1", "Ninja2"]

Esta sería una opción para cumplir con este principio, otra es usar alguna librería funcional que haga esto por nosotros, a mí la que más me gusta es Ramda, es muy eficiente y cumple más o menos bien con los requerimientos funcionales

const R = require('ramda')

const oldNinjas = Object.freeze(['Ninja1', 'Ninja2'])
const newNinja = 'Ninja3'

const totalNinjas = R.append(newNinja, oldNinjas)

console.log(totalNinjas) // result --> [ "Ninja1", "Ninja2", "Ninja3"]
console.log(oldNinjas) // result --> [ "Ninja1", "Ninja2"]

Esto es un ejemplo de como podemos cumplir con el principio de inmutabalidad necesario en la programación funcional.

Creo que por el momento es suficiente para un único post, continuaremos con los siguientes conceptos en proximos capítulos, cuadaaaos y un abrazooooooo