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)