entropyslave
entropyslave

Reputation: 83

Abstract types in method parameters

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

Answers (2)

Miles Sabin
Miles Sabin

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

Rogach
Rogach

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

Related Questions