Reputation: 31754
A simple question. The method ::
of class immutable List
is defined as:
sealed abstract class List[+A] ...
def ::[B >: A](x: B): List[B]
Suppose say I have:
class Fruit
class Mango extends Fruit
scala> val d:List[Fruit] = List.empty[Fruit]
d: List[Fruit] = List()
scala> new Mango :: d
res5: List[Fruit] = List(Mango@272d6774)
Now I am confused here. As per ::
declaration, the argument type should be contravariant. i.e. in this case any class that >: Fruit
(I understand why it is made that way). But what I dont get is, Mango <: Fruit
, so why doesn't the compiler throw error?
Upvotes: 0
Views: 104
Reputation: 170919
In the line
new Mango :: d
you expect the compiler to reason: "new Mango
has type Mango
and d
has type List[Fruit]
, so List[Fruit].::(Mango)
needs to typecheck, which it doesn't". In this case it would indeed be an error.
But in fact, it reasons differently: "I need new Mango :: d
to typecheck, so new Mango
must have some type B
such that B :> Fruit
. Is there such a B
? Yes, B = Fruit
." So there is no error.
Upvotes: 3
Reputation: 13922
You would still be able to add the Mango
to d
even if List
was invariant, simply because Mango
is still a Fruit
. Think about it in terms of a plain old java list:
val d = new java.util.ArrayList[Fruit]
d.add(new Mango)
So the contravariance doesn't restrict your ability to add subtypes - it enables you to add supertypes and get a compiler-checked list type back from it:
val d: List[Fruit] = Nil
val a: List[Plant] = new Carrot :: d
Upvotes: 5