Funtores, Aplicativos y Mónadas en imágenes

Miguel Á. Moreno
Mar 28, 2016 · 10 min read

Es curioso, no se me da bien escribir posts, blogs, ni nada de eso, soy más consumidor que productor. No había escrito en medium tampoco hasta ahora, pero hace unos días me topé con un post sobre Funtores, Aplicativos y Mónadas contados con imágenes que me encantó. Lo he leído un par de veces y me he dado cuenta de que la traducción al español estaba desaparecida, cosas de Internet… Así que he contactado con el creador y me he ofrecido para traducirlo yo mismo, por si a alguien hispano hablante le apetece tenerlo en castellano y compartirlo. Este será mi primer post, por tanto :)

El post recorre lo que son los Funtores, los Aplicativos y las Mónadas con imágenes ilustrativas hechas por el propio autor, así como ejemplos en Haskell, aunque no creo que tengas que saber mucho Haskell para poder seguirlo. Yo he podido seguirlo sin problemas, así que tú también.

El link original al artículo es:

http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html

Espero que os guste tanto como a mí y os animo a seguir el blog de este autor.


Funtores, Aplicativos y Mónadas en imágenes

Una traducción entre mil

Aquí hay un simple valor:

Y sabemos cómo aplicar una función a este valor:

Bastante simple. Vamos a extender esto diciendo que cualquier valor puede estar en un contexto. Por ahora, puedes pensar en ese contexto como en una caja en la que puedes meter ese valor:

Ahora, cuando apliques la función a este valor, obtendrás diferentes resultados dependiendo de ese contexto. Esta es la idea en la que se basan los Funtores, los Aplicativos, las Mónadas y las Flechas (o Morfismos). El tipo `Maybe` define dos contextos relacionados: `Nothing` (que significa Nada) y `Just a` (que significaría algo como “Simplemente a”, siendo a un elemento no definido ahora mismo, será un número, un String… etc).

En un momento vamos a ver como difiere el aplicar una función cuando algo es un `Just` o un `Nothing`. ¡Pero primero vamos a hablar de Funtores!


Funtores

Cuando un valor está envuelto en un contexto, no puedes simplemente aplicarle una función normal:

Ahí es donde entra `fmap`. `fmap` viene del barrio, `fmap` es consciente de los contextos, `fmap` sabe como tiene que aplicar funciones a valores que están envueltos en un contexto. Por ejemplo, supón que quieres aplicar la función `(+3)` a un `Just 2`. Usando `fmap`:

¡Boom! ¡`fmap` nos enseña cómo se hace! Pero, ¿cómo sabe `fmap` cómo aplicar la función?

Pero, primero, ¿qué es realmente un funtor?

Un, Funtor es una `typeclass`, aquí está la definición:

1. Para hacer ‘f’ un Funtor,
2. Ese tipo de datos tiene que definir cómo `fmap` se va a comportar para él

Un Funtor es cualquier tipo de datos que define cómo `fmap` se comporta para su caso. Esto es cómo funciona `fmap`:

1. `fmap` toma una función, como (+3)
2. y un funtor, como `Just 2`
3. y devuelve otro Funtor, como `Just 5`

Así que podemos hacer esto:

Y `fmap` mágicamente aplica la función, porque el tipo `Maybe` es un Funtor. Especifica en su definición cómo se tiene que comportar `fmap` con cosas como `Just` y `Nothing`.

Aquí puedes ver lo que está pasando entre bambalinas cuando escribimos

1. Desenvuelve/extrae el valor de su contexto
2. Aplica la función
3. Vuelve a envolver el resultado en un contexto igual al inicial

Así que te has quedado como… ok, `fmap` ¿puedes aplicarle la función (+3) a un `Nothing`?

1. No hay valor
2. No apliques la función
3. Acabas teniendo nada

No entra nada, no sale nada… ¡no puedo explicar eso!

Como Morpheo en Matrix, `fmap` sabe lo que tiene que hacer: empiezas no teniendo nada, y acabas sin tener nada! `fmap` es zen, es guay. Ahora tiene sentido por qué existe el tipo `Maybe`. Por ejemplo, aquí hay un ejemplo de cómo trabajarías con una base de datos en un lenguaje que no tiene el tipo `Maybe`:

Pero con Haskell:

Si `findPost` devuelve un `post`, entonces obtendremos el título con `gePostTitle`. Si no devuelve nada, ¡entonces no obtendremos nada! Mola, ¿eh?. `<$>` es el símbolo que funciona como versión infija de `fmap` de manera que podemos usarlo así:

Aquí hay otro ejemplo: ¿qué pasa cuando aplicas una función a una lista?

1. Un array de valores

2. Aplica la función a cada valor

3. Un nuevo array de valores

¡Las listas también son funtores! Aquí está la definición:

Okei, okei, un último ejemplo: ¿Qué pasa cuando aplicas una función a otra función?

Aquí hay una función:

- Toma un valor
- Devuelve un valor

Aquí hay una función aplicada a otra función

- Toma un valor
- Devuelve un valor

¡El resultado es simplemente otra función!

¡Así que las funciones son Funtores también!

¡Cuando le aplicas fmap a una función, simplemente estás haciendo composición!


Aplicativos

Los Aplicativos lo llevan al siguiente nivel. Con los Aplicativos, nuestros valores se envuelven en un contexto, igual que con los Funtores:

¡Pero nuestras funciones también se pueden envolver en contextos!

Sip, déjalo reposar. Los Aplicativos no se andan con chiquitas. El módulo de haskell `Control.Applicative` define `<*>`, que sabe cómo aplicar una función envuelta en un contexto a un valor envuelto en un contexto:

1. Una función envuelta en un contexto
2. Un valor en un contexto
3. Desenvuelve tanto el valor como la función y aplícasela al valor
4. Envuelvelo en un conexto otra vez.

Ejemplo

Usar `<*>` puede llevar a situaciones interesantes, por ejemplo:

Aquí hay una cosa que puedes hacer con Aplicativos que no puedes hacer con Funtores. ¿Cómo le aplicas una función que toma dos argumentos a dos valores envueltos?

Aplicativos:

Los Aplicativos se ponen los Funtores a un lado.

diría un Aplicativo.

Y, ¡eh!, hay una función llamada `liftA2` que hace lo mismo!


Mónadas

Cómo aprender algo sobre las Mónadas:

  1. Sácate un doctorado en ingeniería informática.
  2. ¡Tíralo porque no lo necesitas para esta sección!

Las Mónadas añaden un giro de tuerca a lo visto hasta ahora:

Los Funtores aplican una función a un valor envuelto en un contexto:

Los Aplicativos aplican una función envuelta a un valor envuelto:

Las Mónadas aplican, a un valor envuelto, una función que a su vez devuelve un valor envuelto. Las Mónadas tienen una función `>>=` (llamada ‘bind’ y pronunciado “baind”) que hace esto.

Vamos a ver un ejemplo: Nuestra amiga ‘Maybe’ es una Mónada:

Supón que `half` es una función que solamente funciona con números pares

- Toma un valor
- Devuelve un valor envuelto

¿Qué pasa si le metemos un valor envuelto?

Necesitamos utilizar `>>=` para calzarle nuestro valor envuelto a la función. Aquí hay una foto de `>>=`:

Así es como funciona

¿Qué está pasando por ahí dentro? Que `Monad`(Mónada) es otra `typeclass`. Aquí está parte de su definición:

Donde `>>=` es así:

1. >>= recibe una Mónada, como (Just 3)
2. Y una función que devuelve una Mónada (como la función `half`)
3. Y devuelve una Mónada

Así que `Maybe` es una Mónada

¡Y aquí al vemos en acción con un “Just 3”!

1. Bind desenvuelve el valor
2. Le pasa ese valor a la función
3. Devuelve un valor envuelto (ya sea un `Just` o un `Nothing`)

Y si le pasas un `Nothing` es incluso más sencillo:

1. Nada entra
2. No se hace nada
3. Nada sale

También puedes encadenar estas llamadas

¡Mola! Así que ahora sabemos que `Maybe`es un `Funtor`, un `Applicativo` y una `Mónada`.

Ahora vamos a dar un paseito tranquilamente por otro ejemplo, la Mónada IO (de input/output = Entrada/Salida en inglés)

Más específicamente tres funciones:

- `getLine`: No recibe ningún argumento y lee los datos dados por el usuario:

- `readFile`: Toma una cadena de texto (una ruta a un fichero) y devuelve el contenido del fichero.

- `putStrLine`: Recibe una cadena de texto y lo imprime:

Las tres funciones toman un valor normal (o incluso ningún valor) y devuelve uno envuelto. ¡Las podemos enlazar usando `>>=`!

1. Lee los datos del usuario
2. Usalos para leer un fichero
3. ¡Imprime el contenido del fichero!

¡Oh yeah baby! !Buen asiento para ver el espectáculo de las Mónadas!

Haskell viene también con una construcción para las Mónadas, llamada `do-notation`

Esto es equivalente a lo anterior.


Conclusión

  1. Un Funtor es un tipo de datos que implementa la `typeclass` `Functor`
  2. Un Aplicativo es un tipo de datos que implementa la `typeclass` `Applicative`
  3. Una Mónada es un tipo de datos que implementa la `typeclass` `Monad`
  4. Un `Maybe` implementa los tres, por lo que es un Funtor, un Applicativo y una Mónada

¿Cuál es la diferencia entre las 3?

- Funtor
- Aplicativo
- Mónada

  • Funtores: Le aplicas la función a un valor envuelto con `fmap` o `<$>`
  • Aplicativos: Le aplicas una función envuelta a un valor envuelto usando `<*>` o `liftA`
  • Mónadas: Le aplicas una función que devuelve un valor envuelto, a un valor envuelto a su vez, utilizando `>>=` o `liftM`

Así que, queridísimo amigo (creo que ya somos amigos llegados a este punto), creo que ambos estaremos de acuerdo al pensar que las Mónadas son fáciles y una IDEA INTELIGENTE(tm). Ahora que te has probado un poco del pastel con esta guía, por qué no ir de héroe y comerte la tarta entera. échale un ojo a la sección de Mónadas de “Learn you a Haskell for Great Good”. Hay un montón de cosas que yo he pasado por encima porque el autor, Miran Lipovaca, hace un gran trabajo hablando en profundidad sobre el asunto.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store