Programación Funcional en JavaScript: La mónada IO

c2yd4goxuaef6sp

Terminamos esta serie de patrones funcionales con una de las últimas mónadas que vamos a estudiar en esta serie: la mónada IO.

Después de estudiar cuál era la mejor manera de poder encadenar la ejecución de funciones sobre un entorno seguro con Maybe y Either, es el turno para que nos detengamos en cómo podemos hacer que las entradas y salidas de nuestras aplicaciones se encuentren en un contexto funcional. Veremos como las entradas y salidas se convierten en un coladero de efecto laterales y como IO nos salvaguarda en cierta medida.

La mónada IO

Las siguientes funciones:

function write(document, id, value) {
    document.querySelector('#' + id).innerHTML = value;
}

function read(document, id) {
   return document.querySelector('#' + id).innerHtml;
}

Son un quebradero de cabeza en el mundo funcional. Presentan efecto laterales. No podemos asegurar, que si ejecutamos estas funciones de manera reiterativa, obtengamos siempre el mismo valor.

Sin embargo, si desarrollamos aplicaciones reales – y más siendo aplicaciones en web en la parte cliente – donde se hace un uso mayor de interacciones con el usuario, será difícil no hacer uso de ellas.

Es por ello que necesitamos un mecanismo para poder aislar su comportamiento inesperado y poder trabajar con ellas. En lenguajes funcionales contamos con la siguiente estructura:

class IO {
    constructor(effect) {
        if (typeof effect !== 'function) {
            throw 'IO: function required';
        }
        this._effect = effect;
    } 

    static of (value) {
        return new IO(() => value);
    }

    static from(fn) {
        return new IO(fn);
    }

    map(fn) {
        const self = this;
        return new IO(function () {
            return fn(self._effect());
        });
    }

    run() {
       return this.effect();
    }
}

Estudiemos la clase:

  • El constructor nos permite cobijar una función de la que dudamos que no tiene un comportamiento todo lo funcional y puro que nos gustaría.
  • of‘ permite envolver un valor dentro de esta mónada, mientras que ‘from‘ es una manera de esconder la instanciación de la clase en un único punto identificado.
  • map‘ nos permite concatenar funciones. No ejecuta, simplemente va concatenando la estructura interna con la nueva función construyendo una tubería de funciones a ejecutar.
  • run‘ permite esta ejecución en cadena. Esta ejecución perezosa nos permite tener el control sobre en qué momento desencadenar la ejecución de todo el ‘pipeline’.

El comportamiento de esta mónada cambia en relación a las otras ya que no protege un valor sino que conserva una función. Ya que no podemos evitar su comportamiento, envolvemos un valor devuelto o la propia función que se encarga de una entrada/salida sobre esta estructura. De esta forma podemos envolver una función tras otras y ejecutarla cuando deseemos terminar con la tubería.

Veamos un ejemplo:

Obtengamos el título de una web, pongamoslo en mayúsculas y volvamos a ponerlo en su etiqueta h1. Para esto, cogeremos las funciones de lectura y escritura anteriores y las tuneamos un poco para que nos sean útiles.

const write = R.curry(function(document, id, value) {
    document.querySelector('#' + id).innerHTML = value;
});

const read = R.curry(function(document, id) {
   return document.querySelector('#' + id).innerHtml;
});

const writeDom = write(document);
const readDom = read(document);

Las currificamos y le pasamos el ‘document’ para manipular el DOM.

Lo siguiente es crear nuestra función combinada usando nuestra querida mónada IO:

<!-- html -->
<h1 id='tittle'>El Abismo de Null</h1>

// js
const changeToUppercase = IO.from(readDom('tittle'))
                            .map(toUpperCase)
                            .map(writeDom('tittle'));

Con esto lo que podemos hacer es desencadenar la ejecución de las funciones mapeadas de la siguiente manera:

// js
changeToUppercase.run();

<!-- html -->
<h1 id='tittle'>EL ABISMO DE NULL</h1>

Usamos ‘run’ porque, hasta este momento, no se ha ejecutado todavía ninguna de las funciones mapeadas con la estructura monádica. Como pasaba en otras mónada, ‘run’ nos hace un lifting de las capas con la que hemos envuelto el valor impuro como si de una cebolla se tratara.

Como vemos, la gran ventaja de esta mónada es que separa la parte pura de la impura. Tenemos una forma de concatenar cómputos sobre funciones impuras y de una forma más o menos controlada.

Nos leemos 🙂

Anteriores posts de Programación Funcional en JavaScript:

Introducción

  1. La Programación Funcional en JavaScript
  2. Programación Funcional en JavaScript: Los Objetos
  3. Programación Funcional en JavaScript: Las funciones

El control de flujo funcional

  1. Programación Funcional en JavaScript: Los métodos funcionales
  2. Programación Funcional en JavaScript: La recursividad

La modularidad funcional

  1. Programación Funcional en JavaScript: La aridad y las tuplas
  2. Programación Funcional en JavaScript: La currificación
  3. Programación Funcional en JavaScript: La composición
  4. Programación Funcional en JavaScript: Los combinadores

Patrones funcionales

  1. Programación Funcional en JavaScript: Los funtores
  2. Programación Funcional en JavaScript: La mónada Maybe
  3. Programación Funcional en JavaScript: La mónada Either
  4. Programación Funcional en JavaScript: La mónada IO
Anuncios

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