Given the following:
trait Fruit
class Apple extends Fruit
class Orange extends Fruit
case class Crate[T](value:T)
def p(c:Crate[Fruit]) { }
val cra = Crate(new Apple)
val cro = Crate(new Orange)
since Crate is invariant, I can't do the following (as expected):
scala> val fruit:Crate[Fruit] = cra
<console>:10: error: type mismatch;
found : Crate[Apple]
required: Crate[Fruit]
val fruit:Crate[Fruit] = cra
^
scala> val fruit:Crate[Fruit] = cro
<console>:10: error: type mismatch;
found : Crate[Orange]
required: Crate[Fruit]
val fruit:Crate[Fruit] = cro
scala> p(cra)
<console>:12: error: type mismatch;
found : Crate[Apple]
required: Crate[Fruit]
p(cra)
^
scala> p(cro)
<console>:12: error: type mismatch;
found : Crate[Orange]
required: Crate[Fruit]
p(cro)
But why can I call method p with these when Crate is not covariant? :
scala> p(Crate(new Apple))
Crate(line2$object$$iw$$iw$Apple@35427e6e)
scala> p(Crate(new Orange))
Crate(line3$object$$iw$$iw$Orange@33dfeb30)
Have I missed some basic principles of variance?
In the latter cases, the compiler assumes that you want this to work and actually says
p(Crate( (new Apple): Fruit ))
which is perfectly okay. It's the same as if you manually did
val f: Fruit = new Apple // totally fine
p(Crate(f)) // Also totally fine
This is just a small part of the immense wizardry that the compiler applies to try to figure out what you mean with your types without making you type it all out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With