Iota coproducts (or ‘from those Weekdays, these Writers’) – part 2

Previously on Scalera…

Let’s remember that, on previous post, we defined our favourite weekday subset as a co-product:

case object Monday
case object Tuesday
case object Wednesday
case object GroundhogDay

import iota._
import iota.syntax.all._
import TList.::

type Weekday = Cop[Monday.type :: Tuesday.type :: Wednesday.type :: TNil]

Now imagine we wanted to define a serializer for our weekdays…

Writer[T]

Assuming that our serializer is something like this:

trait Writer[T] {
  def write(t: T): String
}
object Writer {
  def apply[T](implicit w: Writer[T]): Writer[T] = w
}

And that it has several weekday defined instances:

implicit val mondayW: Writer[Monday.type] =
  _ => "monday"

implicit val tuesdayW: Writer[Tuesday.type] =
  _ => "TUESDAY"

implicit val wednesdayW: Writer[Wednesday.type] =
  _ => "wed"

implicit val madeUpdayW: Writer[GroundhogDay.type] =
  _ => "nonsense!"

…is not a big deal: a type class with its instances.
The question now is, how can we define a generic serializer for Weekday?

The first part of the party of the first part…

We know that

Weekday = Monday U Tuesday U Wednesday

So if we have defined Writer[Monday.type], Writer[Tuesday.type] and Writer[Wednesday.type], we could say that weekday serializer will be:

WeekdayWriter = Writer[Monday.type] U Writer[Tuesday.type] U Writer[Wednesday.type]

The question is, how can we map each weekday with its serializer? Well, the answer is with the following complex (and simple) snippet:

import TList.Op.Map
type WeekdayWriter = Cop[Map[Writer, Weekday#L]]

We’ve defined the weekdays serializer as the coproduct result of mapping(Map) each T type – contained inside the Weekday TList that composes the co-product(Weekday#L) – to the Writer[T] type.

In other words, internally the compiler has done something remotely similar (mind the gap between types and instances – compile time vs runtime) to:

val weekdays = List(Monday, Tuesday, Wednesday)
val writers = weekdays.map{
  case Monday    => Writer[Monday.type]
  case Tuesday   => Writer[Tuesday.type]
  case Wednesday => Writer[Wednesday.type]
}

From now on, WeekdayWriter can’t be any other type appart from Writer[T] where T is defined inside Weekday.

Yes, but now you have two different unrelated co-products

Yep, true. Let’s establish the relationship.

…shall be known in this contract as the first part of the party of the first part…

We know that we want something similar to this:

def weekdayWriter[D](weekday: D)(implicit stuff): WeekdayWriter = ???

Which implicit evidences do we need?

  • D can be anything, but we need it to have a Writer[D](if it can’t be serialized, it’s worthless)
  • something that tell us that Writer[D] is one of the included writers in WeekdayWriter. As we explained on the previous post, this evidence is Cop.Inject.

So the method would like this:

def weekdayWriter[D](
  weekday: D)(
  implicit w: Writer[D],
  ev: Cop.Inject[Writer[D], WeekdayWriter]): WeekdayWriter = {

  //So if we know that w is injectable into WeekdayWriter...
  w.inject[WeekdayWriter] //Let's inject it
}

we could define a helper for making it even prettier though:

implicit class AnyWeekdayWriter[D](
  day: D)(
  implicit w: Writer[D],
  ev: Cop.Inject[Writer[D], WeekdayWriter]){

  def write: String =
    weekdayWriter(day).value match {
      case w: Writer[D@unchecked] => w.write(day)
    }
}

If we try it, we’ll notice that:

Monday.write //"monday"
Tuesday.write //"TUESDAY"
GroundhogDay.write //NOPE! It doesn't compile

…even though there’s an implicit Writer[GroundhogDay.type], as there’s no evidence that proves that it’s injectable into the WeekdayWriter co-product, it’s not serializable as a Weekday.

So what does this provide?

Let’s suppose the initial model:

sealed trait Weekday
case object Monday extends Weekday
case object Tuesday extends Weekday
case object Wednesday extends Weekday

implicit lazy val mondayW: Writer[Monday.type] = _ => "monday"
implicit lazy val tuesdayW: Writer[Tuesday.type] = _ => "TUES"
implicit lazy val wednesdayW: Writer[Wednesday.type] = _ => "wed"

How would you define a serializer for Weekday? Something logical would be:

implicit def weekdayWriter: Writer[Weekday] = {
  case Monday => mondayW.write(Monday)
  case Tuesday => tuesdayW.write(Tuesday)
  case Wednesday => wednesdayW.write(Wednesday)
}

If we decided to add a new day to the week we’d have to:

  • add the case object
  • make it extend from Weekday
  • add the concrete serializer
  • modify the generic serializer

Whereas the focus we’ve used so far would involve:

  • adding the case object
  • modifying Weekday definition
  • adding the concrete serializer

…okay, that’s not a big deal re-factoring but, what would it happen if we wanted to group those days in another way, for example, by odd days?

With the co-product focus would be as simple as defining the new co-product that stands for the type union of the odd days, as well as the function for getting the implicit Writer[T]:

type OddDay = Cop[Monday.type :: Wednesday.type :: TNil]

type OddDayWriter = Cop[Map[Writer, OddDay#L]]
def odddayWriter[D](
  oddday: D)(
  implicit w: Writer[D],
  ev: Cop.Inject[Writer[D], OddDayWriter]): OddDayWriter = {
  w.inject[OddDayWriter]
}

And in the inheritance world, where there’s suffering and death…

sealed trait Weekday
sealed trait OddDay
case object Monday extends Weekday with OddDay
case object Tuesday extends Weekday
case object Wednesday extends Weekday with OddDay

implicit def weekdayWriter: Writer[OddDay] = {
  case Monday => mondayW.write(Monday)
  case Wednesday => wednesdayW.write(Wednesday)
}

Although this is heavy metal, we hope we’ve helped you having a better understanding about the fascinating coproduct world.

See you next post.
Peace out!

Anuncios

Coproductos en iota (o ‘de aquellos Weekdays, estos Writers’) – parte 2

En anteriores episodios de Scalera…

Recordemos que, en el anterior post, definimos nuestro subconjunto de días de la semana favorito como un co-producto:

case object Monday
case object Tuesday
case object Wednesday
case object GroundhogDay

import iota._
import iota.syntax.all._
import TList.::

type Weekday = Cop[Monday.type :: Tuesday.type :: Wednesday.type :: TNil]

Ahora imaginemos que queremos definir un serializador para nuestros días de la semana…

Writer[T]

Pongamos que nuestro serializador es algo sencillo del estilo:

trait Writer[T] {
  def write(t: T): String
}
object Writer {
  def apply[T](implicit w: Writer[T]): Writer[T] = w
}

Que tiene definidas instancias para nuestros días:

implicit val mondayW: Writer[Monday.type] =
  _ => "monday"

implicit val tuesdayW: Writer[Tuesday.type] =
  _ => "TUESDAY"

implicit val wednesdayW: Writer[Wednesday.type] =
  _ => "wed"

implicit val madeUpdayW: Writer[GroundhogDay.type] =
  _ => "nonsense!"

Nada fuera de lo normal hasta ahora: una type class con sus instancias.
La cuestión ahora es, ¿cómo podemos definir un serializador genérico para Weekday?

La parte contratante de la primera parte…

Sabemos que

Weekday = Monday U Tuesday U Wednesday

Por lo que si tenemos definidos Writer[Monday.type], Writer[Tuesday.type] y Writer[Wednesday.type], podríamos decir que el serializador de días de la semana será:

WeekdayWriter = Writer[Monday.type] U Writer[Tuesday.type] U Writer[Wednesday.type]

La cuestión es, ¿cómo mapeamos cada día de la semana con su serializador?
Con el complejo a la vez que simple snippet:

import TList.Op.Map
type WeekdayWriter = Cop[Map[Writer, Weekday#L]]

Hemos definido el serializador de días de semana como el coproducto resultante de mapear(Map) cada tipo T contenido en la TList de días de la semana que compone el co-producto(Weekday#L) al tipo Writer[T].

Es decir, que el compilador internamente ha hecho algo parecido (salvando obviamente las distancias cuando comparamos tipos con instancias) a:

val weekdays = List(Monday, Tuesday, Wednesday)
val writers = weekdays.map{
  case Monday    => Writer[Monday.type]
  case Tuesday   => Writer[Tuesday.type]
  case Wednesday => Writer[Wednesday.type]
}

A partir de ahora WeekdayWriter no puede ser de ningún otro tipo que no sea Writer[T] donde T está definido en Weekday

Sí, pero ahora tienes dos coproductos sin forma de establecer una relación entre ellos

…ok, cierto.

…será considerada como la parte contratante de la primera parte

Sabemos que queremos algo del estilo:

def weekdayWriter[D](weekday: D)(implicit stuff): WeekdayWriter = ???

¿Qué evidencias implícitas necesitamos?

  • D puede ser cualquier cosa, pero necesitamos que tenga un Writer[D](si no se puede serializar, no nos vale)
  • algo que nos diga que ese Writer[D] es uno de los incluídos en WeekdayWriter. Cómo vimos en el post anterior de esta serie, esa evidencia es Cop.Inject.

Por lo tanto el método nos quedaría como:

def weekdayWriter[D](
  weekday: D)(
  implicit w: Writer[D],
  ev: Cop.Inject[Writer[D], WeekdayWriter]): WeekdayWriter = {

  //So if we know that w is injectable into WeekdayWriter...
  w.inject[WeekdayWriter] //Let's inject it
}

Incluso podríamos definir un helper para hacerlo más bonito:

implicit class AnyWeekdayWriter[D](
  day: D)(
  implicit w: Writer[D],
  ev: Cop.Inject[Writer[D], WeekdayWriter]){

  def write: String =
    weekdayWriter(day).value match {
      case w: Writer[D@unchecked] => w.write(day)
    }
}

Si lo probamos notaremos que:

Monday.write //"monday"
Tuesday.write //"TUESDAY"
GroundhogDay.write //NOPE! It doesn't compile

…incluso aunque hay un Writer[GroundhogDay.type] implícito, al no haber una evidencia que permita inyectarlo en el co-producto de WeekdayWriter, no permite serializarlo.

¿Y qué me aporta?

Supongamos el modelo inicial:

sealed trait Weekday
case object Monday extends Weekday
case object Tuesday extends Weekday
case object Wednesday extends Weekday

implicit lazy val mondayW: Writer[Monday.type] = _ => "monday"
implicit lazy val tuesdayW: Writer[Tuesday.type] = _ => "TUES"
implicit lazy val wednesdayW: Writer[Wednesday.type] = _ => "wed"

¿Cómo definirías un serializador para Weekday? Lo lógico sería hacer algo del estilo:

implicit def weekdayWriter: Writer[Weekday] = {
  case Monday => mondayW.write(Monday)
  case Tuesday => tuesdayW.write(Tuesday)
  case Wednesday => wednesdayW.write(Wednesday)
}

Si resulta que decidiésemos añadir un nuevo día a la semana tendríamos que:

  • añadir el case object
  • hacer que extienda de Weekday
  • añadir el serializador concreto
  • modificar el serializador genérico

Mientras que con el enfoque que hemos utilizado durante estos dos posts:

  • añadir el case object
  • modificar la definición de Weekday
  • añadir el serializador concreto

…venga va, tampoco es que nos ahorremos muchos sitios donde tocar pero, ¿qué ocurriría si quisieramos realizar otra agrupación, por ejemplo, por días impares?

Con el enfoque del co-producto sería tan sencillo como añadir el nuevo co-producto que representa la unión de los tipos de los días impares y la función para obtener el Writer[T]:

type OddDay = Cop[Monday.type :: Wednesday.type :: TNil]

type OddDayWriter = Cop[Map[Writer, OddDay#L]]
def odddayWriter[D](
  oddday: D)(
  implicit w: Writer[D],
  ev: Cop.Inject[Writer[D], OddDayWriter]): OddDayWriter = {
  w.inject[OddDayWriter]
}

Mientras que en el mundo de la herencia, donde hay muerte y sufrimiento…

sealed trait Weekday
sealed trait OddDay
case object Monday extends Weekday with OddDay
case object Tuesday extends Weekday
case object Wednesday extends Weekday with OddDay

implicit def weekdayWriter: Writer[OddDay] = {
  case Monday => mondayW.write(Monday)
  case Wednesday => wednesdayW.write(Wednesday)
}

Aunque es material denso, esperamos haberos ayudado a entender el fascinante mundo de los co-productos

Nos vemos en el próximo post.
¡Agur de limón!

Iota coproducts (or how to make weeks shorter) – part 1

You may (or may not) remember that we occasionally mentioned co-products when we spoke about algebraic data types. We defined them as the natural way of expressing type union(Wine = White U Red U Espumoso). In this post we’ll try to define new enumerations expressed as co-products, for example, weekdays….

Intro: product vs. co-product

Just to sum up and to better understand the example, imagine you have a triplet of certain type elements:

(Int, String, Double)

If the data that stands for a student may be expressed as a triplet of her age AND name AND average score, we could say that a student may be expressed as the product of these three types:

case class Student(
  age: Int,
  name: String,
  avg: Double)

(Surprisingly case classes extend from something called scala.Product, ehem …)

On the other hand, if we’re told that the result of some web request may return the number of error rows OR the value we wanted to retrieve OR the amount of seconds to wait until next request try, we would be talking about a co-product.

Intro: helpless co-products in Scala

In Scala, defining an enumeration as a co-product can be as easy as:

sealed trait Color
case object Blue extends Color
case object Red extends Color
//...

But, what happens when the co-product former types are not related between them (Int, Animal, …)?

Well, we could use Either type:

type Or[A, B] = Either[A, B]
val v1: Int Or String = Left(2)
val v2: Int Or String = Right("hi")

Easy, right? But, what happens now if we want to point that the value could be an Int or String or Boolean? Things become more complicated:

type Or[A, B] = Either[A, B]
val v1: Or[String, Or[Int, Boolean]] = Left("hi")
val v2: Or[String, Or[Int, Boolean]] = Right(Left(2))
val v2: Or[String, Or[Int, Boolean]] = Right(Right(true))

iota co-products

One of the alternatives for avoiding this headache is iota, a library developed by 47deg for defining co-products in an awesome way.
It both supports scalaz and cats. In this post, we’ll see how it’s applicable to cats (because reasons…)

library-dependant

In order to start compiling this, we’ll start by adding some cats-core and iota-core dependencies to our project:

scalacOptions += "-Ypartial-unification" // Needed if you're using Scala 2.11+
libraryDependencies ++= Seq(
  "org.typelevel" %% "cats-core" % "1.0.1",
  "io.frees" %% "iota-core" % "0.3.4"
)

Start point

Let’s suppose we have to define an enumeration.
One of the most common ways to achieve it in Scala, as we mentioned somewhere before, would be with:

sealed trait Weekday
case object Monday extends Weekday
case object Tuesday extends Weekday
case object Wednesday extends Weekday
case object Thursday extends Weekday
case object Friday extends Weekday
case object Saturday extends Weekday
case object Sunday extends Weekday

Nevertheless, let’s skip some extra days (for making it simpler) and let’s remove the inheritance relationship (for making it more interesting):

case object Monday
case object Tuesday
case object Wednesday
case object GroundhogDay //Just for messing things up...

For expressing now the type union ( Weekday = Monday U Tuesday U Wednesday) we define with iota the co-product this way:

// Let's import some iota core stuff (including syntax)
import iota._
import iota.syntax.all._

// ...and also import TList (which it's something really similar to a Shapeless HList)
// it keeps info about the contained element types.
import TList.::

type Weekday = Cop[Monday.type :: Tuesday.type :: Wednesday.type :: TNil]

And after these wonderful mayan glyphs, how do we use the Weekday type?
Because the following definitively doesn’t work:

val day: Weekday = Monday //NOPE! It doesn't fit

For being able to use it, we’ll have to check how to inject Monday.type into our co-product.
If your very first reaction to the previous sentence was ‘eing?’, take a look at the following section about injection and projection concepts. Otherwise, jump onto page 30 for killing the troll skip next section.

Inject & Project

Without giving a full-detail view about injective and projective modules, let’s see an easy example for better understanding these concepts.
Let’s imagine I have defined the weekdays co-product as a case class with all possible values as Options so only one of these can be fulfilled:

// Crazy stuff BTW,
// defining a co-product as a product of Option's
case class Weekday(
  monday: Option[Monday.type] = None,
  tuesday: Option[Tuesday.type] = None,
  wednesday: Option[Wednesday.type] = None){

  require({
    val defined =
      List(monday, tuesday, wednesday)
        .filter(_.isDefined).size
    defined <= 1
  }, "A Weekday can only be one of the options")

}
object Weekday {
  def inject(day: Monday.type): Weekday =
    Weekday(monday=Some(day))

  def inject(day: Tuesday.type): Weekday =
    Weekday(tuesday=Some(day))

  def inject(day: Wednesday.type): Weekday =
    Weekday(wednesday=Some(day))
}

With this definition we try to illustrate that Monday.type is not actually a Weekday sub-type, but there’s a function (inject) that makes us able to create a Weekday from a Monday.type: this function injects the value into the co-product.

The projection, on the other hand, stands for the reverse process.
Within the same example previously showed we can say that, from a Weekday we can project its ‘tuesday’ part. As this field might be empty, we return it as an Option value:

object Weekday {

  // ... all previously defined stuff goes here ...

  def projectM(wd: Weekday): Option[Monday.type] =
    wd.monday

  def projectT(wd: Weekday): Option[Tuesday.type] =
    wd.tuesday

  def projectW(wd: Weekday): Option[Wednesday.type] = 
    wd.wednesday

}

And how can you do this with iota?

Easy peasy! Just by using the Cop.Inject type class, which is used for establishing the relationship between one of the types that composes the co-product and the co-product itself.
In our case, for injecting a Monday into a Weekday, we need the implicit evidence that it’s injectable:

type Weekday = Cop[Monday.type :: Tuesday.type :: Wednesday.type :: TNil]

implicitly[Cop.Inject[Monday.type, Weekday]]

Thanks to this evidence, we’ll be able of doing:

val wd: Weekday = Monday.inject[Weekday]

and also of retrieving the Monday projection from any Weekday:

val wd: Weekday = Tuesday.inject[Weekday]
val monday: Option[Monday.type] =
  Cop.Inject[Monday.type, Weekday].prj(wd)
assert(monday.isEmpty, "It's actually a wednesday!")

At next post, we’ll see how to generate serializers for these co-products: pure gold…

Peace out!

Coproductos en iota (o cómo hacer las semanas más cortas) – parte 1

Quizás recordaréis (o puede que no) que nombramos hace tiempo de pasada los co-productos cuando hablábamos de tipos algebraicos de datos. Los definíamos entonces como la manera natural de expresar unión de tipos (Wine = White U Red U Espumoso). En este post trataremos de definir enumerados expresados como co-productos, como por ejemplo, los días de la semana…

Intro: producto vs. co-producto

Por hacer algo de memoria y entender mejor el ejemplo, imaginad que tenéis una terna de elementos de ciertos tipos:

(Int, String, Double)

Si los datos que representan a un estudiante se pueden expresar como una terna de edad Y nombre Y nota media, podemos decir que un estudiante se expresa como el producto de estos tres tipos:

case class Student(
  age: Int,
  name: String,
  avg: Double)

(Curioso además que las case classes extiendan de algo llamado scala.Product, ehem …)

Si por el contrario nos dicen que el resultado de una llamada web nos puede devolver el número de filas erróneas O el valor que queríamos recuperar O el número de segundos a esperar hasta nueva petición, estamos hablando de un co-producto.

Intro: Co-productos en Scala sin ruedines

En Scala, definir un enumerado como un co-producto puede ser tan sencillo como definir:

sealed trait Color
case object Blue extends Color
case object Red extends Color
//...

Pero, ¿qué ocurre cuando los tipos que forman el co-producto no tienen relación entre sí (Int, Animal, …)?

Bueno, podríamos usar el tipo Either:

type Or[A, B] = Either[A, B]
val v1: Int Or String = Left(2)
val v2: Int Or String = Right("hi")

Fácil, ¿no? Pero, ¿qué ocurre ahora si queremos indicar que el valor puede ser Int o String o Boolean? La cosa se complica un poco más:

type Or[A, B] = Either[A, B]
val v1: Or[String, Or[Int, Boolean]] = Left("hi")
val v2: Or[String, Or[Int, Boolean]] = Right(Left(2))
val v2: Or[String, Or[Int, Boolean]] = Right(Right(true))

iota co-products

Una de las alternativas para evitar este dolor de cabeza es iota, una librería desarrollada por 47deg para definir co-productos de una manera ‘awesómica’.
Tiene soporte tanto para scalaz como para cats. En este post, veremos como aplica a cats (because reasons…)

libro-dependiente

Para que esto empiece a compilar, empezaremos añadiendo a nuestro proyecto las dependencias de cats-core e iota-core:

scalacOptions += "-Ypartial-unification" // Needed if you're using Scala 2.11+
libraryDependencies ++= Seq(
  "org.typelevel" %% "cats-core" % "1.0.1",
  "io.frees" %% "iota-core" % "0.3.4"
)

Punto de partida

Supongamos que tenemos que definir un enumerado.
Una de las formas más comunes de hacerlo en Scala, cómo decíamos algún párrafo más arriba, sería con:

sealed trait Weekday
case object Monday extends Weekday
case object Tuesday extends Weekday
case object Wednesday extends Weekday
case object Thursday extends Weekday
case object Friday extends Weekday
case object Saturday extends Weekday
case object Sunday extends Weekday

No obstante, quitemos algunos días de más (para hacerlo más sencillo) y eliminemos la relación de herencia que los relaciona (para hacerlo más interesante):

case object Monday
case object Tuesday
case object Wednesday
case object GroundhogDay //Just for messing things up...

Para expresar ahora la unión de tipos ( Weekday = Monday U Tuesday U Wednesday) con iota definimos el co-producto como sigue:

// Let's import some iota core stuff (including syntax)
import iota._
import iota.syntax.all._

// ...and also import TList (which it's something really similar to a Shapeless HList)
// it keeps info about the contained element types.
import TList.::

type Weekday = Cop[Monday.type :: Tuesday.type :: Wednesday.type :: TNil]

Y después de estos bonitos glifos mayas, ¿cómo usamos el tipo Weekday?
Porque esto definitivamente no funciona:

val day: Weekday = Monday //NOPE! It doesn't fit

Para poder usarlo, tendremos que ver cómo inyectar el tipo Monday.type en nuestro co-producto.
Si tu primera impresión a la frase anterior ha sido ‘eing?’, dale una ojeada a la siguiente sección sobre los conceptos de inyección y proyección. En caso contrario, salta a la página 30 para matar al troll puedes pasar a la siguiente.

Inject & Project

Sin entrar en mucho detalle sobre módulos inyectivos y proyectivos, veamos un sencillo ejemplo para entender los conceptos.
Imaginemos que yo hubiera definido el co-producto de los días de la semana como una case class con todos los posibles valores representados por un Option, de manera que solo uno de ellos puede estar relleno:

// Crazy stuff BTW,
// defining a co-product as a product of Option's
case class Weekday(
  monday: Option[Monday.type] = None,
  tuesday: Option[Tuesday.type] = None,
  wednesday: Option[Wednesday.type] = None){

  require({
    val defined =
      List(monday, tuesday, wednesday)
        .filter(_.isDefined).size
    defined <= 1
  }, "A Weekday can only be one of the options")

}
object Weekday {
  def inject(day: Monday.type): Weekday =
    Weekday(monday=Some(day))

  def inject(day: Tuesday.type): Weekday =
    Weekday(tuesday=Some(day))

  def inject(day: Wednesday.type): Weekday =
    Weekday(wednesday=Some(day))
}

Con esta definición pretendemos ilustrar que Monday.type realmente no es un subtipo de Weekday, pero existe una función (inject) que nos permite crear un Weekday a partir de un Monday.type: esta función inyecta el valor en el co-producto.

La proyección, por otra parte, representa el proceso inverso.
En el mismo ejemplo de antes, podemos decir que, a partir de un Weekday, podemos proyectar su parte ‘tuesday’. Como podría estar vacío para ese campo, devolvemos el Option (que ya es por definición):

object Weekday {

  // ... all previously defined stuff goes here ...

  def projectM(wd: Weekday): Option[Monday.type] =
    wd.monday

  def projectT(wd: Weekday): Option[Tuesday.type] =
    wd.tuesday

  def projectW(wd: Weekday): Option[Wednesday.type] = 
    wd.wednesday

}

Y esto en iota, ¿cómo se hace?

Pues easy peasy, utilizando la type class Cop.Inject, que se utiliza para establecer la relación entre el tipo que compone el co-producto y el co-producto en sí.
En nuestro caso, para poder inyectar un Monday en un Weekday, necesitamos la evidencia implícita de que es inyectable :

type Weekday = Cop[Monday.type :: Tuesday.type :: Wednesday.type :: TNil]

implicitly[Cop.Inject[Monday.type, Weekday]]

Mediante dicha evidencia, seremos capaces de hacer:

val wd: Weekday = Monday.inject[Weekday]

Y también de obtener la proyección de Monday sobre cualquier Weekday:

val wd: Weekday = Tuesday.inject[Weekday]
val monday: Option[Monday.type] =
  Cop.Inject[Monday.type, Weekday].prj(wd)
assert(monday.isEmpty, "It's actually a wednesday!")

En la próxima entrega de la serie, veremos cómo generar serializadores para estos co-productos: canela fina…

¡Agur de limón!