sophiecollard / variance

Simple examples to illustrate the differences between invariance, covariance and contravariance in Scala

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Variance

Simple examples to illustrate the differences between invariance, covariance and contravariance in Scala.

Example 1

Consider the following Animal type and Rescue and Clinic type classes:

sealed abstract class Animal(val name: String)

object Animal:

  final case class Cat(
    override val name: String,
    livesRemaining: Int
  ) extends Animal(name)

  final case class Dog(
    override val name: String,
    breed: Option[DogBreed]
  ) extends Animal(name)
trait Rescue[+A]:
  extension (name: String) def adopt: A

trait Clinic[-A]:
  extension (a: A) def examine: String

Now, let's define methods to adopt an animal and take a dog to the vet:

def adopt(name: String)(using rescue: Rescue[Animal]): Animal =
  rescue.adopt(name)

def takeToTheVet(dog: Dog)(using clinic: Clinic[Dog]): String =
  clinic.examine(dog)

Assuming instances of Rescue[Animal] and Clinic[Dog] have been defined, we could invoke the above methods as follows:

val teddy = adopt("Teddy")
println(s"Welcome home ${teddy.name}!")

val médor = Dog(name = "Médor", breed = Some(DogBreed.Labrador))
takeToTheVet(médor)

Let's assume however, that we do not have Rescue[Animal] nor Clinic[Dog] instances. Instead, all we have are Rescue[Dog] and Clinic[Animal]:

val teddy = adopt("Teddy")(using summon[Rescue[Dog]])
println(s"Welcome home ${teddy.name}!")

val médor = Dog(name = "Médor", breed = Some(DogBreed.Labrador))
takeToTheVet(médor)(using summon[Clinic[Animal]])

The code above is able to compile thanks to Rescue being covariant in A and Clinic being contravariant in A.

About

Simple examples to illustrate the differences between invariance, covariance and contravariance in Scala

License:Apache License 2.0


Languages

Language:Scala 100.0%