Scala types: Class A is not equal to the T where T is: type T = A

杀马特。学长 韩版系。学妹 提交于 2019-12-19 19:17:11

问题


I was reading the section 20.7 of the book Programming in Scala and I was wondering why while this code compiles:

class Food
class Fish extends Food
class Grass extends Food

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


class Cow extends Animal {
  type SuitableFood = Grass
  override def eat(food: Grass) {}
}


val bessy: Animal = new Cow

bessy eat (new bessy.SuitableFood)

This code does not (the rest of the code is the same as before, only the last line changes):

bessy eat (new Grass)

And as far as I understand the type of Grass is the same of Cow.SuitableFood.

Also, I have another question regarding this example:

If bessy is of type Animal, how can the compiler know that it needs a type SuitableFood -> Grass instead of a type Food? 'Cause trying to provide a new Food gives me a compile error of type mismatch, but the class Animal needs a type Food and the type of bessy is explicitly defined: Animal


回答1:


It's because bessie is declared Animal rather than Cow. bessie.SuitableFood is a "path-dependent type" (see below).

Try this:

val clarabelle: Cow = new Cow

clarabelle eat (new Grass)

This works because the compiler can deduce that clarabelle.SuitableFood = Grass from clarabelle's declared type.

Since bessie is declared Animal, not Cow, the compiler can't safely deduce that bessie.SuitableFood = Grass.* When you say new bessie.SuitableFood, the compiler generates code to look at the actual bessie object and generate a new instance of the appropriate type. bessie.SuitableFood is a "path-dependent type": the "path" (the bessie. part) that leads to the last identifier (SuitableFood) is actually part of the type. This enables you to have a custom version of a type for each individual object of the same class.


*Well, actually, I think that if the compiler were a little smarter, it could deduce that bessie.SuitableFood = Grass, since bessie is a val, not a var, and therefore won't change its type. In other words, the compiler ought to know that even though bessie is declared Animal, she's really a Cow. Perhaps a future version of the compiler will make use of this knowledge, and perhaps there's a good reason why that wouldn't be a good idea, which someone more expert than I will tell us. (Postscript: One just did! See Travis Brown's comment below.)




回答2:


Regarding the second part of your question: it doesn't. Animal doesn't specify that its food is Food, but some subtype of Food. Would the compiler accept this, code like your example would compile, and wrongly so. The compiler doesn't know that the necessary subtype is Grass (which is why eat(new Grass) doesn't work either), it just knows that there are some foods your cow can't eat and is cautious about it.




回答3:


I believe bessy eat (new bessy.SuitableFood) compiling is a bug (which was fixed in 2.11). Because another subtype of Animal could have a SuitableFood for which new makes no sense, e.g. type SuitableFood = Food or even type SuitableFood = Food with Int (Food with Int is a perfectly nice subtype of Food!).



来源:https://stackoverflow.com/questions/32161100/scala-types-class-a-is-not-equal-to-the-t-where-t-is-type-t-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!