Scala Language Type Variance Covariance


Example

The + symbol marks a type parameter as covariant - here we say that "Producer is covariant on A":

trait Producer[+A] {
  def produce: A
}

A covariant type parameter can be thought of as an "output" type. Marking A as covariant asserts that Producer[X] <: Producer[Y] provided that X <: Y. For example, a Producer[Cat] is a valid Producer[Animal], as all produced cats are also valid animals.

A covariant type parameter cannot appear in contravariant (input) position. The following example will not compile as we are asserting that Co[Cat] <: Co[Animal], but Co[Cat] has def handle(a: Cat): Unit which cannot handle any Animal as required by Co[Animal]!

trait Co[+A] {
  def produce: A
  def handle(a: A): Unit
}

One approach to dealing with this restriction is to use type parameters bounded by the covariant type parameter. In the following example, we know that B is a supertype of A. Therefore given Option[X] <: Option[Y] for X <: Y, we know that Option[X]'s def getOrElse[B >: X](b: => B): B can accept any supertype of X - which includes the supertypes of Y as required by Option[Y]:

trait Option[+A] {
  def getOrElse[B >: A](b: => B): B
}