cats.Semigroupal es una clase de tipos que nos permite combinar contextos. Si tenemos dos objetos de tipo F[A] y F[B], un Semigroupal[F] nos permite combinarlos para formar un F[(A, B)]. Su definición en Cats es:
trait Semigroupal[F[_]] {
def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
}
Como comentamos en un post anterior, los parámetros fa y fb son independientes entre sí: podemos calcularlos en cualquier orden antes de pasarlos al producto. Esto contrasta con flatMap, que impone un orden estricto en sus parámetros. Esto nos da más libertad cuando definimos instancias de Semigroupal que cuando definimos Monads.
Mientras que Semigroup nos permite unir valores, Semigroupal nos permite unir contextos. Juntemos algunas Opciones como ejemplo:
import cats.Semigroupal
import cats.instances.option._ // for Semigroupal
Semigroupal[Option].product(Some(123), Some("abc"))
// res1: Option[(Int, String)] = Some((123, "abc"))
Si ambos parámetros son instancias de Some, terminamos con una tupla de los valores dentro. Si alguno de los parámetros se evalúa como None, el resultado completo es None:
Semigroupal[Option].product(None, Some("abc"))
// res2: Option[Tuple2[Nothing, String]] = None
Semigroupal[Option].product(Some(123), None)
// res3: Option[Tuple2[Int, Nothing]] = None
El objeto complementario de Semigroupal define un conjunto de métodos además del producto. Por ejemplo, los métodos tuple2 a tuple22 generalizan el producto a diferentes aridades:
import cats.instances.option._ // for Semigroupal
Semigroupal.tuple3(Option(1), Option(2), Option(3))
// res4: Option[(Int, Int, Int)] = Some((1, 2, 3))
Semigroupal.tuple3(Option(1), Option(2), Option.empty[Int])
// res5: Option[(Int, Int, Int)] = None
Los métodos map2 a map22 aplican una función especificada por el usuario a los valores dentro de 2 a 22 contextos:
Semigroupal.map3(Option(1), Option(2), Option(3))(_ + _ + _)
// res6: Option[Int] = Some(6)
Semigroupal.map2(Option(1), Option.empty[Int])(_ + _)
// res7: Option[Int] = None
También existen métodos contramap2 a contramap22 e imap2 a imap22, que requieren instancias de Contravariant e Invariant respectivamente.
Solo hay una ley para Semigroupal: el método del producto debe ser asociativo.
product(a, product(b, c)) == product(product(a, b), c)