The language reference says:
- The variance position of a method parameter is the opposite of the variance position of the enclosing parameter clause.
- The variance position of a type parameter is the opposite of the variance position of the enclosing type parameter clause.
- The variance position of the lower bound of a type declaration or type parameter is the opposite of the variance position of the type declaration or parameter.
OK what does it mean for a type parameter to have a variance position?
class Moo[+A, -B] {
def foo[X] (bar : Y) ...
So Y is in a contravariant position, this is clear. We can put B in its position, but not A.
But what does it mean for X to be in a contravariant position? We cannot substitute A or B or anything there, it's just a formal parameter!
That's true, but this thing can have subordinate positions which are types, and have variance. So we need to count the position of X when tracking how many times we flip variance. There's no subordinate clauses of X here, but consider this:
class Moo[+A, -B] {
def foo[X >: Z] (bar : B) ...
We probably can replace Z with either A or B, but which is correct? Well, the position of Z is the opposite of that of X, and the position of X is the opposite of that of the top-level, which is covariant, so Z must be covariant too. Let's check:
abstract class Moo[+A, -B] {
def foo[X >: A] (bar : B)
}
defined class Moo
Looks like we are right!