Covariance makes subtype relationships flow in the same direction through a parameterized type: if Dog is a subtype of Animal, List[Dog] is a subtype of List[Animal]. Scala marks a covariant parameter with +, as in enum List[+A]. section 3.1
Empty values and subtype flexibility
Nil extends List[Nothing]. Because Nothing is a subtype of every type and List is covariant, an empty list can be used as List[Int], List[Double], or another List type. For example: val ex1: List[Double] = List.Nil. section 3.1
Covariance supports several practical design choices:
- It improves type inference for constructors such as Nil and for APIs built from covariant data types. section 15.2 section 3.1
- Leaving out + makes the parameter invariant, so the subtype relationship no longer follows automatically. section 3.1
- Pull[+O, +R] uses covariance in both output and result types, allowing Result[+R] to be Pull[Nothing, R] and Output[+O] to be Pull[O, Unit]. section 15.2
Listing 15.2 The Pull data type
enum Pull[+O, +R]:
case Result[+R](result: R) extends Pull[Nothing, R]
case Output[+O](value: O) extends Pull[O, Unit]
case FlatMap[X, +O, +R](
source: Pull[O, X],
f: X => Pull[O, R]) extends Pull[O, R]
def step: Either[R, (O, Pull[O, R])] = this match #1
case Result(r) => Left(r)
case Output(o) => Right(o, Pull.done)
case FlatMap(source, f) =>
source match
case FlatMap(s2, g) =>
s2.flatMap(x => g(x).flatMap(y => f(y))).step
case other => other.step match
case Left(r) => f(r).step
case Right((hd, tl)) => Right((hd, tl.flatMap(f)))
@annotation.tailrec
final def fold[A](init: A)(f: (A, O) => A): (R, A) = #2
step match
case Left(r) => (r, init)
case Right((hd, tl)) => tl.fold(f(init, hd))(f)
def toList: List[O] = #3
fold(List.newBuilder[O])((bldr, o) => bldr += o)(1).result
def flatMap[O2 >: O, R2](
f: R => Pull[O2, R2]
): Pull[O2, R2] = #4
Pull.FlatMap(this, f)
def >>[O2 >: O, R2](next: => Pull[O2, R2]): Pull[O2, R2] =
flatMap(_ => next)
def map[R2](f: R => R2]): Pull[O, R2] =
flatMap(r => Result(f(r)))
Inspect the covariant Pull definition and its Result and Output constructors to see covariance applied to both type parameters. section 15.2


