Cats proporciona una sintaxis de aplicación simplificada. Importamos cats.syntax.apply.
import cats.instances.option._ // for Semigroupal
import cats.syntax.apply._ // for tupled and mapN
El método tuplado se agrega implícitamente a la tupla de Opciones. Utiliza el Semigroupal para Option para comprimir los valores dentro de las Opciones, creando una sola Opción de una tupla:
(Option(123), Option("abc")).tupled
// res8: Option[(Int, String)] = Some((123, "abc"))
Podemos usar el mismo truco en tuplas de hasta 22 valores. Cats define un método tuplado separado para cada aridad:
(Option(123), Option("abc"), Option(true)).tupled
// res9: Option[(Int, String, Boolean)] = Some((123, "abc", true))
Además de tuplado, la sintaxis de aplicación de Cats proporciona un método llamado mapN que acepta un Funtor implícito y una función de la aridad correcta para combinar los valores.
final case class Cat(name: String, born: Int, color: String)
(
Option("Garfield"),
Option(1978),
Option("Orange & black")
).mapN(Cat.apply)
// res10: Option[Cat] = Some(Cat("Garfield", 1978, "Orange & black"))
De todos los métodos mencionados aquí, el más común es usar mapN. Internamente, mapN usa Semigroupal para extraer los valores de Option y Functor para aplicar los valores a la función.
Es bueno ver que esta sintaxis está marcada. Si proporcionamos una función que acepta el número o tipo de parámetros incorrectos, obtenemos un error de compilación:
val add: (Int, Int) => Int = (a, b) => a + b
// add: (Int, Int) => Int = <function2>
(Option(1), Option(2), Option(3)).mapN(add)
// error: ':' expected but '(' found.
// Option("Garfield"),
// ^
(Option("cats"), Option(true)).mapN(add)
/ error: ':' expected but '(' found.
// Option("Garfield"),
// ^
Apply también tiene métodos contramapN e imapN que aceptan funtores contravariantes e invariantes. Por ejemplo, podemos combinar Monoids usando Invariant. Aquí hay un ejemplo:
import cats.Monoid
import cats.instances.int._// for Monoid
import cats.instances.invariant._// for Semigroupal
import cats.instances.list._// for Monoid
import cats.instances.string._// for Monoid
import cats.syntax.apply._// for imapN
final case class Cat(
name: String,
yearOfBirth: Int,
favoriteFoods: List[String]
)
val tupleToCat: (String, Int, List[String]) => Cat = Cat.apply _
val catToTuple: Cat => (String, Int, List[String]) = cat => (cat.name, cat.yearOfBirth, cat.favoriteFoods)
implicit val catMonoid: Monoid[Cat] = (
Monoid[String],
Monoid[Int],
Monoid[List[String]]
).imapN(tupleToCat)(catToTuple)
Nuestro Monoid nos permite crear Cats "vacíos" y agregar Cats usando la sintaxis :
import cats.syntax.semigroup._ // for |+|
val garfield= Cat("Garfield", 1978, List("Lasagne"))
val heathcliff = Cat("Heathcliff", 1988, List("Junk Food"))
garfield |+| heathcliff
// res14: Cat = Cat("GarfieldHeathcliff", 3966, List("Lasagne", "Junk
Food"))