Dima
Dima

Reputation: 40500

Another (simpler) trouble with type variance

I had a (looong) version of this problem posted yesterday here: Trouble with type variance

To cut the (really) long story short, this:

 class A[-P, T <: P]

does not compile (it complains, that "P occurs in covariant position in type <: P of type T"). An answer to my earlier question suggested that the declaration is indeed invalid, because A[Foo, Bar] would be a subclass of A[Nothing, Bar], but the latter is illegal (Bar isn't a subclass of Nothing).

I don't think that's reason enough. By that logic, something like this should be illegal too: class B[+T <: String] - B[String] should be a subclass of B[Any] but the latter is invalid.

Moreover, this:

class C[T, -P >: T] 

Actually does compile. Isn't this essentially the same exact thing as A above?

Upvotes: 1

Views: 37

Answers (1)

HTNW
HTNW

Reputation: 29193

These are subtly different.

class Test[-P, T <: P]

says "let P be a type parameter that varies contravariantly and unboundedly, and let T be a subtype of P." That is, T is completely ignored when checking whether a type is valid for P. Only then is T constrained to be a subtype of P. This is illegal, for the reason you already know. You can start with Test[Any, String], and because P is unboundedly contravariant, it can become Test[Nothing, String], and suddenly you have String <: Nothing.

class tseT[T, -P :> T] // Code highlighters HATE him. Click to find out why.

says "let T be a type parameter that doesn't vary and is unbounded, and then let P be a contravariant type parameter that is lower-bounded by T." This is tiny bit different, because this time P's variance is constrained by the lower bound T. P still varies contravariantly, but it cannot vary lower than T, or else it will violate P >: T. This is safe, because you can start with tseT[String, Any], and then P can only vary down as long as it is still a supertype of String, so it is illegal for P to vary down to Nothing.

Note that type parameters are processed left-to-right. This is why the first example is declared invalid, instead of inferring the bound P >: T, because T is not declared yet when P is processed, and such a bound would be a forward-reference anyway.

Upvotes: 1

Related Questions