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?
The problem here is that your val
cowis typed asAnimalrather thanCow, so all that the compiler knows is that itseatmethod expects some specific subtype ofFood, but it doesn’t know which, and in particular it’s unable to prove that that type is equal toGrass.You can see the difference this makes to the type of the method (as viewed from
Animalvs. as viewed fromCow) by asking for it’s eta-expansion,You’ll notice that in the second, more precisely typed, case the compiler views the method as taking an argument of type
Grassrather than the abstract typecow.Feed.