CovariantDecoder

Let's follow the compiler's advice and create a new CovariantDecoder[+A], along with a CovariantCatDecoder instance that extends it, as shown in the following code:

trait CovariantDecoder[+A] {
def decode(s: String): Option[A]
}
object CovariantCatDecoder extends CovariantDecoder[Cat] {
(...)
}

We do not show the implementation of decode in CovariantCatDecoder; it is the same as in InvariantCatDecoder. With this covariant parameter, the following relationship is verified:

implicitly[CovariantDecoder[Cat] <:< CovariantDecoder[Animal]]

This time, we can assign the CovariantCatDecoder to an instance of CovariantDecoder[Animal], as shown in the following code:

val covariantAnimalDecoder: CovariantDecoder[Animal] = CovariantCatDecoder
covariantAnimalDecoder.decode("Cat(Ulysse)")
// res0: Option[Animal] = Some(Cat(Ulysse)))

When we call decode on it, we get back an Option[Animal].

At first, glance, having CovariantDecoder seems natural—if my decoder can produce Cat, and Cat is an Animal, my decoder should also be a decoder of Animal. On the other hand, if I have an instance of Decoder[Animal], I would expect it to be able to decode any Animal—not only Cat, but also Dog instances—and this is not the case for our earlier covariantAnimalDecoder.

There is no right or wrong design here; this is just a matter of taste. In general, I would advise you to use invariant type parameters first, and if you then experience some limitations with it, you can decide to make them covariant or contravariant.

The full covariant implementation for getting Cat and Dog would be the following:

object CovariantCatAndDogDecoder extends CovariantDecoder[Animal] {
val CatRegex = """Cat((w+))""".r
val DogRegex = """Dog((w+))""".r
def decode(s: String): Option[Animal] = s match {
case CatRegex(name) => Some(Cat(name))
case DogRegex(name) => Some(Dog(name))
case _ => None
}
}

val covariantCatsAndDogsDecoder = CovariantCatAndDogDecoder

covariantCatsAndDogsDecoder.decode("Cat(Garfield)")
// res4: Option[Animal] = Some(Cat(Garfield)))
covariantCatsAndDogsDecoder.decode("Dog(Aiko)")
// res5: Option[Animal] = Some(Dog(Aiko)))
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset