Translate

lunes, 1 de agosto de 2022

Mónadas en Cats parte 19

cats.data.Reader es una mónada que nos permite secuenciar operaciones que dependen de alguna entrada. Las instancias de Reader envuelven funciones de un argumento, brindándonos métodos útiles para componerlas.

Un uso común para Readers es la inyección de dependencia. Si tenemos varias operaciones que dependen de alguna configuración externa, podemos encadenarlas usando un Reader para producir una gran operación que acepte la configuración como un parámetro y ejecute nuestro programa en el orden especificado.

Podemos crear un Reader[A, B] a partir de una función A => B usando el constructor Reader.apply:


import cats.data.Reader

final case class Cat(name: String, favoriteFood: String)

val catName: Reader[Cat, String] = Reader(cat => cat.name)

// catName: Reader[Cat, String] = Kleisli(<function1>)


Podemos extraer la función nuevamente usando el método de ejecución del Reader's y llamarla usando apply:


catName.run(Cat("Garfield", "lasagne"))

// res1: cats.package.Id[String] = "Garfield"


El poder de Readers proviene de sus métodos map y flatMap, que representan diferentes tipos de composición de funciones. Por lo general, creamos un conjunto de Readers que aceptan el mismo tipo de configuración, los combinamos con map y flatMap, y luego llamamos a ejecutar para inyectar la configuración al final.

El método map simplemente extiende el cálculo en el Reader al pasar su resultado a través de una función:


val greetKitty: Reader[Cat, String] = catName.map(name => s"Hello ${name}")

greetKitty.run(Cat("Heathcliff", "junk food"))

// res2: cats.package.Id[String] = "Hello Heathcliff"


El método flatMap es más interesante. Nos permite combinar Readers que dependen del mismo tipo de entrada. Para ilustrar esto, ampliemos nuestro ejemplo de saludo para alimentar también al gato:


val feedKitty: Reader[Cat, String] = Reader(cat => s"Have a nice bowl of ${cat.favoriteFood}")

val greetAndFeed: Reader[Cat, String] =

for {

greet <- greetKitty

feed <- feedKitty

} yield s"$greet. $feed."

greetAndFeed(Cat("Garfield", "lasagne"))

// res3: cats.package.Id[String] = "Hello Garfield. Have a nice bowl of lasagne."

greetAndFeed(Cat("Heathcliff", "junk food"))

// res4: cats.package.Id[String] = "Hello Heathcliff. Have a nice bowl of junk food."