Path Dependent Types example doesn't work

2019-09-08 15:39发布

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条回答
一纸荒年 Trace。
2楼-- · 2019-09-08 16:14
  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.

查看更多
登录 后发表回答