Reputation: 83
There's a simple example in Programming in Scala by Odersky et al on abstract types, but it does not seem to follow to it's logical conclusion [now edited to make this my exact code]:
class Food
class Grass extends Food
class FishFood extends Food
abstract class Animal {
type Feed <: Food
def eat(food: Feed)
}
class Cow extends Animal {
type Feed = Grass
override def eat(food: Grass) = {}
}
class Test extends App {
val cow: Animal = new Cow
cow.eat(new FishFood)
cow.eat(new Grass)
}
They explain this will prevent me doing (as above):
val cow: Animal = new Cow
cow.eat(new FishFood)
So far so good. But the next natural step does not seem to work either:
cow.eat(new Grass)
I get a compile error:
type mistmatch;
found : Grass
required: Test.this.cow.Feed
cow.eat(new Grass)
^
But cow.Feed is Grass, so why doesn't this work?
Upvotes: 4
Views: 176
Reputation: 23046
The problem here is that your val cow
is typed as Animal
rather than Cow
, so all that the compiler knows is that its eat
method expects some specific subtype of Food
, but it doesn't know which, and in particular it's unable to prove that that type is equal to Grass
.
You can see the difference this makes to the type of the method (as viewed from Animal
vs. as viewed from Cow
) by asking for it's eta-expansion,
scala> val cow: Animal = new Cow
cow: Animal = Cow@13c02dc4
scala> cow.eat _
res12: cow.Feed => Unit = <function1>
scala> cow.asInstanceOf[Cow].eat _
res13: Grass => Unit = <function1>
You'll notice that in the second, more precisely typed, case the compiler views the method as taking an argument of type Grass
rather than the abstract type cow.Feed
.
Upvotes: 8
Reputation: 27190
And it should fail in such way - and actually, I think it is explained in Programming in Scala (2nd edition, p. 460).
On this line:
val cow: Animal = new Cow
You told the compiler to assume that cow
could be any animal, including fish - and it certainly would do fish no good to eat grass!
If you would let the compiler to infer the proper type, it would compile:
val cow = new Cow
cow.eat(new Grass)
Upvotes: 3