liveBook · Manning

Variance

As explained in Functional Programming in Scala, Second Edition

Variance is a type-system property describing how subtyping between parameterized types relates to subtyping between their type arguments.

Variance describes how subtyping between type arguments affects subtyping between parameterized types. With covariance, declaring List[+A] makes List[Dog] a subtype of List[Animal] whenever Dog is a subtype of Animal. section 3.1

Why covariance helps

Nil contains no elements, so Scala can treat it as List[Int], List[Double], or another List type. This follows from Nil extending List[Nothing], Nothing being a subtype of every type, and List being covariant. Omitting the + would make List invariant and remove that subtype relationship. section 3.1

Covariance also lets a Free[Console, A] become a Free[[x] =>> Console[x] | Files[x], A], because Console is accepted as part of the larger effect algebra. However, flatMap exposes F as an input to a function, placing it in a contravariant position. Scala therefore requires a widened F2 supertype and returns Free[F2, B]. section 13.5.1

When working with a covariant parameter:

  • Use the subtype relationship to replace manual union conversions when a smaller effect algebra must become a larger one. section 13.5.1
  • Treat compiler variance errors as signals to widen the affected type parameter, as flatMap does with F2. section 13.5.1
  • Remember that annotations are optional in some definitions, though omitting them can worsen type inference. section 3.1
enum Free[+F[_], A]:                          #1
  case Return(a: A) extends Free[Nothing, A]  #2
  case Suspend(s: F[A])
  case FlatMap[F[_], A, B](
    s: Free[F, A],
    f: A => Free[F, B]) extends Free[F, B]
 
  def flatMap[F2[x] >: F[x], B](
    f: A => Free[F2, B]): Free[F2, B] =       #3
    FlatMap(this, f)

Inspect the covariant Free declaration, its Nothing case, and the widened flatMap signature together. section 13.5.1

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Defines covariance with List, then works through union conversion and a compiler-driven flatMap correction.
Examples
Demonstrates List[Dog] and List[Animal], Nil with Nothing, and Free programs combining Console and Files.
What it assumes
Assumes familiarity with parameterized types and subtyping while introducing covariance through List and Free examples.
What you can do after
Diagnose variance errors and widen type parameters when covariant types occur in contravariant positions.
How it connects
Connects variance to subtyping, type parameters, union types, function types, and free algebras.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Programming in Scala, Second Edition. The section links point to the parts of the book it draws on.