Translate

miércoles, 30 de noviembre de 2022

Semigroupal, Parallel y Applicative parte 3

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"))