Translate

lunes, 16 de mayo de 2022

Mónadas en Cats parte 4

Si bien solo hemos hablado de flatMap anteriormente, el comportamiento monádico se captura formalmente en dos operaciones:

  • una operación de tipo A => F[A];
  • flatMap, de tipo (F[A], A => F[B]) => F[B].

La primera operación proporciona una forma de crear un nuevo contexto monádico a partir de un valor simple. flatMap proporciona el paso de secuenciación que ya hemos discutido, extrayendo el valor de un contexto y generando el siguiente contexto en la secuencia. Aquí hay una versión simplificada de la clase de tipos Monad en Cats:

trait Monad[F[_]] {

    def pure[A](value: A): F[A]

    def flatMap[A, B](value: F[A])(func: A => F[B]): F[B]

}

pure y flatMap deben obedecer un conjunto de leyes que nos permiten secuenciar operaciones libremente sin problemas técnicos ni efectos secundarios no deseados: 

Identidad izquierda: llamar a pure y a flatMap con func es lo mismo que llamar a func:

pure(a).flatMap(func) == func(a)

Identidad derecha: pasar pure a flatMap es lo mismo que no hacer nada:

m.flatMap(pure) == m

Asociatividad: flatMap sobre dos funciones f y g es lo mismo que flatMap sobre f y luego flatMap sobre g:

m.flatMap(f).flatMap(g) == m.flatMap(x => f(x).flatMap(g))

Toda mónada es también un funtor. Podemos definir map de la misma manera para cada mónada usando los métodos existentes, flatMap y pure:

trait Monad[F[_]] {
    def pure[A](a: A): F[A]
    def flatMap[A, B](value: F[A])(func: A => F[B]): F[B]
    def map[A, B](value: F[A])(func: A => B): F[B] = flatMap(value)(a => pure(func(a)))
}