Monadas & Functores para dummies

jueves, 22 de diciembre de 2022

Monadas y functores simplifican la transformación y aplicación de operaciones en el código.

¿Te has preguntado alguna vez cómo manejar datos y operaciones de manera consistente y predecible en tu código? ¿Te gustaría tener una manera de simplificar el flujo de control en tus aplicaciones? Si es así, entonces las monadas y los functores son conceptos que definitivamente debes conocer.

¿Que son?

Las monadas y los functores son herramientas fundamentales de la programación funcional que nos permiten transformar y aplicar operaciones de manera consistente, lo que nos ayuda a escribir código más fácil de mantener y menos propenso a errores. En este artículo, te presentaremos los conceptos básicos de las monadas y los functores y te mostraremos cómo puedes utilizarlos en tu propio código para mejorar su calidad y simplicidad.

Ventajas

Sus ventajas son transformar y aplicar operaciones, consistente y predecible, sobre los datos. Para que el código se más fácil de mantener y sea menos propenso a errores. Esto es especialmente útil cuando se trabaja con aplicaciones complejas. Donde es importante asegurarse de que el flujo de datos y la lógica están correctamente manejados.

Las implementaciones varían según el lenguaje de programación. En JS es posible por su variedad de especificaciones de scope y funciones o clases.

Functores

Los functores son un “objeto” que tiene una función de transformación. Primero toma un valor, luego los transforma y por último lo devuelve encapsulado en un contexto. Esto permite que sea predecible y fácil de mantener.

JS
      
      const Option = value => ({
    value,
    map: fn => Option(value ? fn(value) : value)
});

const some =  value => Option(value);
const none = () => Option(null);  

const  optionOperation = some(5)
                            .map(x => x  *  2)
                            .map(x => x  +  1);

if (optionOperation.value) {
    console.log(optionOperation.value);  // 11
} else {
    console.log("Error");
}
    

En el ejemplo se ha creado un functor: Option. Que se utiliza para manejar valores “nulos” o “None”. Y hay dos functores: some ynone que nos ayudan a saber sobre datos posibles y otro para datos nulos.

Mientras, dentro del propio lenguaje de JS, un ejemplo de un functor sería principalmente las listas (arrays). Que tiene métodos para transformar en el mismo contexto los datos contenidos.

Monadas

Las monadas es un tipo de dato que permite controlar el flujo de datos y su manipulación. Con ello aplicar transformaciones y operaciones. Un ejemplo para ver mejor el funcionamiento. Seria de “Maybe” que permite realizar operaciones verificando los valores nulos e indefinidos.

JS
      
      const  Maybe  =  value => ({
    value,
    map: fn => Maybe(value ? fn(value) : value),
    chain:  fn => value ? fn(value) : Maybe(value),
    fold: (fnSome, fnNone) => value ? fnSome(value) : fnNone()
});

const  maybeOperation  =  Maybe(5)
                            .map(x => x  *  2)
                            .chain(x => Maybe(x  +  3));

const  result  =  maybeOperation.fold(
    value => value,
    () => "Error"
);
    
console.log(result);  // 13
    

En el ejemplo, se genera la monada a partir del valor 5. Y se aplican dos transformaciones a la monada usando el método map y chain (para cuando se use otra monada). Luego, se procesa el valor final empleando el método fold y resultado final es 13.

Un ejemplo cotidiano de las monadas son las promesas. Puesto que permite ejecutar transformaciones y operaciones en un mismo contexto. Esto le permite ser reutilizables y consistentes. Y similar como la monada “Maybe”, permite controlar los valores según su naturaleza.

React

Otros ejemplos prácticos de monadas y functores, se puede llevar a la librería de React, en un ejemplo de carga de datos, para poder tener la información mientras carga y además prediciendo cualquier error.

JS
      
      // utils.js

export const Maybe = value => ({
  value,
  map: fn => Maybe(value ? fn(value) : value),
  chain: fn => value ? fn(value) : Maybe(value),
  fold: (fnSome, fnNone) => value ? fnSome(value) : fnNone()
});

export const Option = value => ({
  value,
  map: fn => Option(value ? fn(value) : value)
});

export const some = value => Option(value);
export const none = () => Option(null);
    
JS
      
      // useFetch.js

import { useState } from 'react';
import { Maybe, some, none } from "./utils";

export const useFetch = url => {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const json = await response.json();
        setData(json);
      } catch (e) {
        setError(e);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, [url]);

  return Maybe(loading)
    .chain(loading => loading ? Maybe(null) : Maybe({ data, error }))
    .fold(
      ({ data, error }) => some({ data, error }),
      () => none()
    );
};
    
JSX
      
      // MyComponent.js
import { useFetch } from "./useFetch"

const MyComponent = () => {
  const fetchResult = useFetch('https://jsonplaceholder.typicode.com/todos');

  return fetchResult.map(({ data, error }) => {
    if (error) {
      return <div>Error: {error.message}</div>;
    } else if (data) {
      return <div>Data: {JSON.stringify(data)}</div>;
    } else {
      return <div>Loading...</div>;
    }
  });
};
    

En este ejemplo, hemos creado dos monadas, Maybe y Option, y dos funciones, some y none, para crear opciones con o sin valores. Luego, hemos generado un hook personalizado useFetch que se encarga de realizar la solicitud de datos y devuelve una monada Maybe con el estado de carga.

En conclusión, las monadas y los functores son una parte esencial de la programación funcional y pueden ser una gran adición a nuestro arsenal de herramientas de programación. Y además podemos saber sobre las nuevas características de JS de la programación funcional.

Lee más artículos