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.