La Programación Funcional en JavaScript

5482493912_317210223b_b.jpg

Cada eslabón, una función. Cada cadena, una aplicación.

Hoy empiezo, en el blog, una nueva serie de posts orientados a mostrar todo aquello que estoy aprendiendo, durante estos últimos meses, sobre programación funcional en JavaScript.

La serie va estar compuesta por un total de 8 posts. Estos posts explicarán todos aquellos conceptos que se nos suelen resistir cuando intentamos encarar un nuevo desarrollo bajo el prisma de la programación funcional.

Para realizar esta serie, me voy a apoyar en este libro. El libro es uno de los referentes habituales en programación funcional, con una curva de aprendizaje ideal para perfiles noveles e intermedios en esta técnica de programación.

El capítulo de hoy nos servirá como introducción de la serie, como la base que nos guíe hacia nuestro objetivo. Nos ayudará en el futuro a entender todos los términos y técnicas necesarios en programación funcional.

Espero que la idea os guste y que lo disfruteis tanto como yo. Empecemos:

¿Qué es la Programación Funcional?

A la hora de encarar nuestros desarrollos podemos seguir técnicas y acercamientos muy diferentes entre sí. Estas diferencias suelen denominarse paradigmas y suponen una forma de pensamiento y de diseño lógico específica para cada una de ellas.

Nuestra forma habitual de programar, esa que llevamos años perfeccionando desde que empezamos en este mundillo, suele ser por medio enlazar sentencia una detrás de otra, sabiendo que se van a ir ejecutando de una forma secuencial de arriba a abajo. Este estilo de programación es lo que conocemos como programación imperativa.

Sin embargo, no es la única forma de desarrollar aplicaciones y otros estilos de programar llevan ya años poniéndose en uso, la programación funcional es uno de ellos. La programación funcional es aquel estilo de desarrollo software que hace mayor énfasis en el uso de funciones. El objetivo de la programación funcional (a partir de ahora PF) es la de abstraernos del control de flujo y de las operaciones de una aplicación por medio de funciones para conseguir que evitemos todos los efectos secundarios provocados por el uso de estados globales y mutables.

La PF es una forma de programar que nos ayuda en aspectos muy importantes para los programadores como son la posibilidad de escribir código de una forma más expresiva. El código expresivo ayuda a que podamos entender mejor lo que se está intentando desarrollar. Cuanto más expresivo es nuestro código, es decir, cuanto más se parezca nuestro código al lenguaje natural, mejor para su legibilidad.

La PF también nos ayuda a crear aplicaciones más limpias, modulables y testeables. Quizá en otros paradigmas también, pero como veremos a lo largo de la serie con ejemplos y comparaciones con otros paradigmas, la PF lo realiza de una forma elegante y robusta.

Nos ayuda, como decíamos anteriormente, a cometer menos errores. Su propia naturaleza hace que sea más complicado realizar ciertas acciones para que nos equivoquemos.

Incluso la PF es un buen mecanismo para protegernos del propio JavaScript. Muchos de los problemas que presenta este lenguaje de programación vienen provocados por dos factores: un mal diseño de ciertas funcionalidades y un mal uso del lenguaje.

JavaScript es un lenguaje muy voluble que permite trabajar con estilos muy diversos. Sin embargo, programar de forma funcional en JavaScript, hará que saquemos mayor partido del lenguaje y que aprovechemos mejor muchas de sus posibilidades.

Como veis, con los conocimientos que tenemos de PF, nos da para hacer una definición de lo que es bastante vaga y difusa. Para definir mejor lo que es la PF,antes necesitamos entender estos cuatro aspectos fundamentales:

  • Programación declarativa
  • Funciones puras
  • Transparencia referencial
  • Inmutabilidad.

Entremos un poco en detalle en cada uno de estos términos:

Programación declarativa

La PF se encuentra dentro del conjunto de los lenguajes de programación declarativos. Si anteriormente explicábamos que la programación imperativa se caracterizaba por indicar al ordenador cómo debía hacer las cosas, la programación declarativa se basa en indicar al ordenador qué debe hacer.

Para explicar esto, es mejor que lo veamos con un ejemplo. Vamos a obtener el cuadrado de todos los elementos de un array numérico. Primero escribamos la solución típica al estilo imperativo:

var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i = 0; i < array.length; i++) {
    array[i] = Math.pow(array[i], 2);
}
array; // [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Creamos un array, lo recorremos y vamos asignando el nuevo valor en el índice correspondiente. Como vemos, estamos en todo momento indicando cómo se tienen que hacer las cosas. Sin embargo, si ese código lo convertimos al estilo declarativo, optariamos por una opción parecida a esta:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(num => Math.pow(num, 2));
// [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

En esta ocasión hemos cedido la gestión del bucle a otra parte del sistema (la función map se encarga de ello). La programación declarativa se centra en el uso de expresiones para describir la lógica de un programa sin tener que especificar el flujo o los cambios de estados que se producen, eso se delega en una función. De esta forma obtenemos un código de primer nivel encargado de ir expresando qué necesita hacer.

Un ejemplo de programación declarativa es SQL. SQL es un lenguaje encargado de describir sentencias que indican que colecciones quiero obtener de una base de datos. En ninguno momento nosotros podemos saber cual es el mecanismo por el cual los datos son obtenidos. Conseguimos abstraernos por medio de cajas negras. La programación declarativa y por tanto la funcional es una experta en el trabajo con cajas negras.

La mejor forma de conseguir cajas negras perfectas en por medio del uso de funciones puras.

Funciones puras

La PF se basa en la premisa de poder construir programas inmutables por medio de pequeños bloques lógicos. Estos pequeños bloques de lógica lo que llamamos funciones.

Dentro de lo posible, tenemos que intentar conseguir desarrollar funciones lo más puras posibles. Una función pura se caracteriza por las siguientes cualidades:

  1. Una función pura solo depende de los parámetros de entrada proporcionados y no de ningún estado externo a la función que podría cambiar durante la evaluación o entre llamadas.
  2. Una función pura no inflige cambios más allá de su ámbito. Por lo tanto, no tiene que modificar estados globales, ni estados que han pasado por referencia.

Veamos un ejemplo de una función. Pensemos si esta función se denominaría pura o impura dentro de la PF:

var likes = 0;
function addLike() {
    return ++likes;
}

Si seguimos la definición de función pura, sabemos al instante que esta función no cumple con lo explicado. La función addLike está modificando estados que no se encuentran en su ámbito y hace uso de una variable externa.

Con JavaScript tenemos que tener mucho cuidado con este tipo de aspecto pues es bastante normal el hacer uso de variables de ámbitos padre de una función, por lo que hay que corregir este comportamiento para que consigamos funciones puras.

Las funciones puras nos ayudan a cometer menos errores y a cerciorarnos en todo momento como es el estado antes y después de ejecutar una función pura. Evitar este tipo de uso, hará que nuestro código sea más robusto y sea más fácil de testear. Para corregir la función anterior, lo que haríamos sería lo siguiente:

function addLike(likes) {
    return ++likes;
}
addLike(addLike(0)); // 2

La función ahora no puede sufrir efectos secundarios porque tiene su estado bajo control, no depende de ningún estado externo.

Para que en el futuro podamos reconocer que puede provocar un efecto secundario en nuestro código, hay que tener en cuenta estas situaciones:

  • Cuando cambiamos una variable, propiedad o dato estructurado global.
  • Cuando cambiamos un valor original del argumento de una función.
  • Cuando procesamos datos de entrada del usuario.
  • Cuando lanzamos excepciones.
  • Cuando mostramos datos por pantalla.
  • Cuando consultamos el DOM del HTML, las cookie o una base de datos.

Muchos de estos efectos secundarios no podremos evitarlos ya que necesitamos realizar estas acciones si queremos acceder aplicaciones reales. Sin embargo, la PF nos va a ayudar a como delimitar estas partes y como tenerlas localizadas para que produzcan en menor daño posible.

Transparencia referencial

Otra de las características de las funciones puras es lo que llamamos transparencia referencial. Este término es la manera formal de explicar todo lo que dijimos en el apartado anterior.

Una función es referencialmente transparente si de manera consistente devuelve los mismos resultados con los mismos datos de entrada. Por lo tanto, seguimos con lo mismo, si tu función depende de estados externos, es complicado que dada unos valores de entrada, siempre devuelva los mismos resultados de salida ya que en el camino de la caja negra, todas esas dependencias pueden cambiar el comportamiento de la función.

Inmutabilidad

El mayor número de bugs en una aplicación está producido por un mal control de los estados de una aplicación. Que un estado pueda cambiar puede hacer que no obtengamos lo esperado. Por lo tanto controlar los cambios de estados siempre tienen que ser de alto grado de preocupación en nuestra aplicación.

La aplicación perfecta sería aquella en la cual no existirían estados y si existiesen que los tengamos lo más controlados posibles. Si pudiésemos hacer que no cambiarán a lo largo de la aplicación mejor que mejor.

Lógicamente nuestras aplicaciones intentan abstraer problemas de la vida real donde existen datos que cambian y donde estos estados tenemos que mostrarselos a los usuarios. Por lo que lo único que podemos hacer es reducir el número de estados a los indispensables e intentar que los estados sean inmutables.

Los estados inmutables ayudan a las funciones puras a devolver los valores esperados. Son grandes aliados de la transparencia referencial. Dado unos datos de entrada, yo te devuelvo unos resultados de salida diferentes.

En JavaScript tenemos problemas con esto. Los tipos simples como String, Number o Boolean son tipos inmutables. Cuando se modifica un estado se genera una nueva referencia del dato en memoria. Sin embargo, los Array y los Objetos son mutables. La referencia a su memoria siempre es la misma, si modificamos un atributo o un elemento el puntero continua.

Esto puede provocarnos efectos secundarios ya que si a una función que creemos pura, le pasamos un objeto o array como parámetro, puede que internamente estemos modificando valores internos y que el estado de nuestra aplicación cambie, provocando que se rompa la transparencia referencial.

Iremos aprendiendo técnicas para evitar manipular estas estructuras de dato lo menos posible y mostraremos librerías que nos ayuden a hacer a los Arrays y Objetos inmutables.

Entonces ¿Qué es en realidad la Programación Funcional?

Después de adentrarnos un poco más en sus aspectos más básicos ya nos encontramos en condiciones de formular una definición mejor:

La Programación Funcional se refiere a la evaluación declarativa de funciones puras para crear programas inmutables y así evitar efectos secundarios que son observados externamente.

Casi nada.

Los beneficios de la Programación Funcional

Como veis, tendremos que cambiar nuestra forma de abstraer si queremos hacer uso de la PF. Hacer uso de la PF nos va a beneficiar porque nos va a aportar una serie de técnicas nuevas que nos ayudan a resolver problemas que en JavaScript hemos tenido siempre.

Por ejemplo y para acabar, centrémonos para este post en estas dos técnicas que se desprenden del conocimiento de la PF:

La PF alienta a la descomposición de tareas complejas

O el divide y vencerás que siempre nos inculcan. Si la unidad de trabajo es la función y estamos hablando que una función pura tiene que hacer uso del menor número de estados posibles y de ser así que sean inmutables, es de entender que la PF va a favorecer mucho a que descomponemos nuestro código en funciones que hagan acciones lo más concretas posibles.

En la PF se da una dicotomía curiosa: Pensar de manera funcional es un juego entre la descomposición y la composición. Descomponemos nuestro código en unidades de trabajo mínimas y componemos nuestros programas para que hagan lo que necesitamos a partir de estas funciones. 

La composición de funciones es clave y tenemos que beneficiarnos de ella para hacer que nuestros aplicativos están formados como piezas de un puzzle que se interconectan entre si para crear un flujo.

Entender la composición es clave para aprender como implementar la modularidad y la reutilización en programas funcionales. Todo lo que aprendamos durante estos posts irá muy dirigido a aprender buenas técnicas de descomposición y composición.

La PF nos permite procesar datos como un flujo en cadena

Las técnicas de Fluent Chain o Fluent API son ya muy conocidas en la comunidad con soluciones como LinQ en .Net o como en jQuery y Gulp en JavaScript.

Para conseguir este efecto en cadena, la idea es hacer que funciones de primer orden compartan un objeto común que permita encadenar este flujo de datos. Esta posibilidad nos muestra un código muy expresivo y declarativo ya que solo tenemos que preocuparnos de ir ejecutando transformaciones sobre nuestros datos. Nuestros datos se encuentran en un Stream donde se ejecutan transformaciones.

Pongamos un ejemplo en código imperativo y presentemos luego su forma funcional:

let enrollment = [
    { enrrolled: 2, grade: 100 },
    { enrrolled: 2, grade: 80 },
    { enrrolled: 1, grade: 89 },
];

var totalGrades = 0;
var totalStudentsFound = 0;
for (let i = 0; i < enrollment.length; i++) {
    let student = enrollment[i];
    if (student !== null) {
        if (student.enrolled > 1) {
             totalGrades += student.grade;
             totalStudentsFound++;
        }
    }
}
var average = totalGrades  / totalStudentsFound; // 90

Y la forma funcional:

_.chain(enrollment)
    .filter(student => student.enrolled > 1)
    .pluck('grade')
    .average()
    .value(); // 90

Como véis el código funcional es mucho más entendible y expresivo. No nos preocupamos por cómo se hacen las transformaciones sino que nos preocupamos sobre que acciones queremos realizar.

Es parecido a lo que buscamos con las promesas en JavaScript. Escribir llamadas asíncronas de una forma funcional y encadenada.

Conclusión

La Programación Funcional no va a ser mejor ni peor que otras técnica o estilos. Simplemente va a ser otra herramienta que nos pueda ayudar en nuestro camino. No es necesario que a partir de ahora dejemos de lado todo lo aprendido y empecemos como locos a crear todo de manera funcional. Muchas veces muchos paradigmas pueden convivir y lo que es bueno de manera funcional, no lo tiene que ser en otro caso. Por lo tanto, como siempre, cuidado y responsabilidad.

Nos espera un viaje trepidante en el blog. Como veis todavía no hemos hablado de temas específicos sobre JavaScript, pero habrá tiempo para explayarnos. Por ahora es importante que tengamos esta base para el resto de la serie.

Para terminar…

…os dejo el mapa conceptual que he realizado para prepararme el post. Quizá os pueda servir a alguno de vosotros:

la-programacion-funcional

Si queréis aprender a realizar vuestros propios mapas conceptuales, Roberto Luis Bisbé nos enseña en este posts.

Nos leemos 🙂

Imagen de portada | flickr

Anuncios

19 comments

  1. Pingback: Programación Funcional en JavaScript: Los Objetos | el.abismo = de[null]
  2. Pingback: Programación funcional en JavaScript: Las funciones | el.abismo = de[null]
  3. Pingback: Programación Funcional en JavaScript: Los métodos funcionales | el.abismo = de[null]
  4. Pingback: Programación Funcional en JavaScript: La recursividad | el.abismo = de[null]
  5. Pingback: Programación Funcional en JavaScript: La aridad y las tuplas | el.abismo = de[null]
  6. Pingback: Programación Funcional en JavaScript: La currificación | el.abismo = de[null]
  7. Pingback: Programación Funcional en JavaScript: La composición | el.abismo = de[null]
  8. Pingback: Programación Funcional en JavaScript: Los combinadores | el.abismo = de[null]
  9. Pingback: Programación Funcional en JavaScript: Los funtores | el.abismo = de[null]
  10. R · enero 10

    Muy bueno

    Le gusta a 1 persona

  11. Pingback: Programación Funcional en JavaScript: La mónada Maybe | el.abismo = de[null]
  12. Pingback: Programación Funcional en JavaScript: La mónada Either | el.abismo = de[null]
  13. Pingback: Programación Funcional en JavaScript: La mónada IO | el.abismo = de[null]
  14. Pingback: Programación Funcional en JavaScript: La evaluación perezosa | el.abismo = de[null]
  15. Pingback: Programación Funcional en JavaScript: La memoización | el.abismo = de[null]
  16. Aaron Planell López · febrero 21

    ¡Genial! Muy buena introducción y muy bien explicada.

    Sólo dos cosas en el código que he visto:
    1) Hay un código que dice:
    — array[i] = Math.por(array[i], 2);
    — En vez de por es pow.
    2) Hay otro código que indica:
    — var avarage = totalGrades / totalStudentsFound; // 90
    — En vez de avarage, es average.

    ¡Un saludo!

    Le gusta a 1 persona

    • jdonsan · febrero 21

      Muchas gracias por tu feedback Aaron. Ya he corregido los errores que me comentas, mil gracias.

      Me gusta

      • Aaron Planell López · febrero 21

        ¡No hay de qué! Le echaré un ojo a los posts. Si quieres, elimina mi comentario original ahora que ya está corregido 😀

        Le gusta a 1 persona

  17. jdonsan · febrero 21

    No hay problema en que se quede, el mérito fue tuyo por verlo 😉 Un saludo

    Me gusta

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s