Variance
Depending on the variance of the type variables in a class declaration, Generic Types can have different subtyping relationships.
The most common variance is Invariance. It is declared by leaving out any variance annotations in Type Parameter declarations. For example, the
List[T]
class is defined asinterface List<T> extends Collection<T>
Because of the missing variance annotation, the
List
type is invariant. This means the following fails:class A
class B extends A
let listB: List<B> = ...
let listA: List<A> = listB // error: incompatible types
The reason for this is that although
B
is a sub-type of A
, List<B>
is not a sub-type of List<A>
.The second type of variance is Covariance. It declares that if a generic type
C<T>
is a sub-type of C<S>
if T
is a sub-type of S
. A common example for this is the Tuple.Of2
class:The tuple class
dyvil.tuple.Tuple.Of2
is defined asclass Of2<+T, +U>
The type variables
A
and B
are both covariant, as denoted by the +
variance annotation. This defines the subtyping relation for the parameterized type Tuple.Of2
as follows (the syntax (T, U)
is equivalent to Tuple.Of2<T, U>
):(T, U) <: (X, Y) if T <: X && U <: Y
Example:
let tupleBB: (B, B) = ...
let tupleAB: (A, B) = tupleBB // fine!
let tupleBA: (B, A) = tupleBB // fine!
let tupleAA: (A, A) = tupleBB // fine!
// however:
tupleBA = tupleAB // error: A is not a sub-type of B
The third variance type and opposite of Covariance is Contravariance. It is denoted by a
-
minus sign before the Type Parameter.interface Function<-T, +U>
The subtyping relationship of contravariant generics is exactly the opposite of that of covariant ones. A generic type
C[T]
is a sub-type of C[S]
if S
is a sub-type of T
(the syntax T -> U
is equivalent to Function.Of1<T, U>
)(T -> U) <: (X -> Y) if T >: X && U <: Y
Example:
let functionBB: B -> B = ...
let functionAB: A -> B = functionBB // error: B is not a super-type of A
let functionBA: B -> A = functionBB // error: A is not a sub-type of B (covariant!)
let functionAA: A -> A = functionBB // error: s.a.
// but:
functionBB = functionAB // fine: A is a super-type of B
The last example can be explained like this: A function that can take an
A
parameter could also handle a B
parameter. Thus, the functionAB
can be used in place of functionBB
.Last modified 4yr ago