0
votes

Scala n00b here. Pretty sure I understand PDT's but was making sure and hit a problem; here's a previous question Scala types: Class A is not equal to the T where T is: type T = A with sample code which I'll reproduce here:

1: class Food
2: class Fish extends Food
3: class Grass extends Food
4:
5: abstract class Animal {
  6: type SuitableFood <: Food
  7: def eat(food: SuitableFood)
8: }
9: 
10: class Cow extends Animal {
  11: type SuitableFood = Grass
  12: override def eat(food: Grass) {}
13: }
14: 
15: val bessy: Animal = new Cow  // [1]
16: 
17: bessy eat (new bessy.SuitableFood) // [2]

The original poster said this compiled, I believe it should, but it won't. If I paste it into the scala REPL it successfully creates bessy [1]:

scala> val bessy: Animal = new Cow
bessy: Animal = Cow@165b8a71

but [2], gives me an error which I don't understand:

scala> bessy.eat(bessy.SuitableFood)
<console>:17: error: value SuitableFood is not a member of Animal
       bessy.eat(bessy.SuitableFood)
                       ^

If I paste it into a file and 'scalac' it, I get the same. Why? bessy is a cow object, type SuitableFood = Grass is defined therein, bessy.SuitableFood is a a class type (isn't it?). What's wrong?

1

1 Answers

2
votes
  1. You are missing new in (new bessy.SuitableFood).

  2. After you fix this, bessy is defined to have type Animal, not Cow, so the compiler doesn't know bessy.SuitableFood is Grass: it's just an abstract type, so new bessy.SuitableFood doesn't work (like new A doesn't when A is a type parameter). E.g. consider that some other subtype of Animal could declare type SuitableFood = Food, and new Food is illegal. I've checked, and it does compile in 2.10.6, but I believe it's a bug which was fixed.

Hence I thought "new bessy.SuitableFood" would return a suitable food because .SuitableFood would be a kind of virtual type, so when is 'Cow' then 'SuitableFood' would be 'grass'. I don't see the value of PDTs if they don't do this.

The point of PDTs here is that if you do have a bessy.SuitableFood, bessy can eat it; and she can't eat spot.SuitableFood (unless the compiler can statically know it's a subtype). But this example doesn't give any way to produce a bessy.SuitableFood, because there is no reason to assume (and no way to tell the compiler) type SuitableFood is a class with a public parameterless constructor. You can fix it by adding a method to Animal:

abstract class Animal {
  type SuitableFood <: Food
  def eat(food: SuitableFood)
  def newSuitableFood(): SuitableFood
}

class Cow extends Animal {
  ...
  def newSuitableFood() = new Grass
}

Now bessy.eat(bessy.newSuitableFood) will compile and work.