Learning path

Full curriculum

Full curriculum

Arrows go from each prerequisite to the units that depend on it. Hover or focus a unit to highlight its path.

Unit content

Variance in parameterized and function types

Variance describes how a subtype relationship between component types changes when those types appear inside a larger type constructor.

Suppose

$$Cat<:Animal.$$

A read-only container can often be covariant:

$$Box(Cat)<:Box(Animal),$$

because anything read from a Box(Cat) is also an Animal.

A write-only consumer is naturally contravariant. A function that can accept every Animal can certainly be used where only Cat values will be supplied.

Function types combine both directions. If

$$S_1<:T_1$$

and

$$T_2<:S_2,$$

then

$$T_1\to T_2 <: S_1\to S_2.$$

Function arguments are contravariant; return values are covariant. A replacement function must accept at least everything the caller may provide and may promise a more specific result.

Mutable references are commonly invariant. Treating Ref(Cat) as Ref(Animal) would allow writing a Dog into storage later read as a Cat.

Variance is therefore not arbitrary annotation syntax. It follows from how values of a parameterized type are produced and consumed, and it explains many otherwise surprising generic-subtyping restrictions.