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 as
interface 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 as
class 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
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
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.