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?
You are missing new
in (new bessy.SuitableFood)
.
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.