Translate

miércoles, 25 de mayo de 2022

Mónadas en Cats parte 8

Ya demostramos la sintaxis de map y flatMap de Cats al escribir un método que abstraía sobre diferentes mónadas:

import cats.Monad

import cats.syntax.functor._ // for map

import cats.syntax.flatMap._ // for flatMap

def sumSquare[F[_]: Monad](a: F[Int], b: F[Int]): F[Int] =

for {

x <- a

y <- b

} yield x*x + y*y

Este método funciona bien en Opcion y List, pero no podemos llamarlo pasando valores simples:

sumSquare(3, 4)
// error: no type parameters for method sumSquare: (a: F[Int], b: F[
Int])(implicit evidence$1: cats.Monad[F])F[Int] exist so that it
can be applied to arguments (Int, Int)
...

Sería increíblemente útil si pudiéramos usar sumSquare con parámetros que estuvieran en una mónada o que no estuvieran en ninguna mónada. Esto nos permitiría abstraernos sobre código monádico y no monádico. Afortunadamente, Cats proporciona el tipo de identificación para cerrar la brecha:

import cats.Id
sumSquare(3 : Id[Int], 4 : Id[Int])
// res1: Id[Int] = 25

Id nos permite llamar a nuestro método monádico usando valores simples. Sin embargo, la semántica exacta es difícil de entender. ¡Convertimos los parámetros a sumSquare como Id[Int] y recibimos un Id[Int] como resultado! ¿Qué está sucediendo? Aquí está la definición de Id para explicar:

package cats
type Id[A] = A

id es en realidad un alias de tipo que convierte un tipo atómico en un constructor de tipo de parámetro único. Podemos emitir cualquier valor de cualquier tipo a una identificación correspondiente:

"Dave" : Id[String]
// res2: Id[String] = "Dave"
123 : Id[Int]
// res3: Id[Int] = 123
List(1, 2, 3) : Id[List[Int]]
// res4: Id[List[Int]] = List(1, 2, 3)

Cats proporciona instancias de varias clases de tipos para Id, incluidos Functor y Monad. Estos nos permiten llamar map, flatMap y pure con valores simples:

val a = Monad[Id].pure(3)
// a: Id[Int] = 3
val b = Monad[Id].flatMap(a)(_ + 1)
// b: Id[Int] = 4
import cats.syntax.functor._ // for map
import cats.syntax.flatMap._ // for flatMap
for {
x <- a
y <- b
} yield x + y
// res5: Id[Int] = 7

La capacidad de abstracción sobre el código monádico y no monádico es extremadamente poderosa. Por ejemplo, podemos ejecutar código de forma asincrónica en producción usando Future y sincrónicamente en prueba usando Id.