Scala类型:类A不等于T,其中T为:类型T=A
我正在阅读《Scala编程》一书的第20.7节,我想知道为什么在编译这段代码时:Scala类型:类A不等于T,其中T为:类型T=A,scala,types,path-dependent-type,Scala,Types,Path Dependent Type,我正在阅读《Scala编程》一书的第20.7节,我想知道为什么在编译这段代码时: 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 ov
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)
据我所知,草的类型与牛的类型相同。适宜的食物
另外,关于这个例子,我还有一个问题:
如果bessy属于动物类型,编译器如何知道它需要一种类型SuitableFood->Grass而不是一种类型Food?”因为试图提供一种新的食物给了我一个类型不匹配的编译错误,但是类Animal需要一种类型的食物,bessy的类型被明确定义:Animal这是因为bessie被声明为
Animal
,而不是Cow
bessie.SuitableFood
是一种“路径依赖型”(见下文)
试试这个:
val clarabelle: Cow = new Cow
clarabelle eat (new Grass)
这是因为编译器可以从clarabelle
的声明类型推断出clarabelle.SuitableFood=Grass
由于bessie
被声明为Animal
,而不是Cow
,编译器无法安全地推断bessie.SuitableFood=Grass
*当您说new bessie.SuitableFood
时,编译器生成代码以查看实际的bessie
对象并生成适当类型的新实例bessie.SuitableFood
是一种“路径依赖型”:导致最后一个标识符(SuitableFood
)的“”(bessie.部分)实际上是该类型的一部分。这使您能够为同一类的每个单独对象拥有类型的自定义版本
*实际上,我认为如果编译器稍微聪明一点,它可以推断出
bessie.SuitableFood=Grass
,因为bessie
是val
,而不是var
,因此不会改变它的类型。换句话说,编译器应该知道,即使贝西被声明为动物,她实际上是一头奶牛。也许编译器的未来版本将利用这些知识,也许有一个很好的理由说明这不是一个好主意,这是一个比我更专业的人告诉我们的。(附言:有一个刚刚做到了!见下面特拉维斯·布朗的评论。)关于你问题的第二部分:没有Animal
没有指定它的食物是food
,而是food
的某些子类型。编译器会接受这一点吗,就像你的例子会编译的代码一样,而且是错误的。编译器不知道必需的子类型是Grass
(这就是eat(newgrass)
也不起作用的原因),它只知道有些食物你的牛不能吃,并且对此很谨慎。我相信bessy eat(new bessy.SuitableFood)
编译是一个bug(在2.11中修复)。因为另一种动物的子类型可能有适合的食物
,而新的
则毫无意义,例如类型适合的食物=食物
,甚至类型适合的食物=含Int的食物
(含Int的食物是食物
的完美子类型!).关于您的脚注:如果您在某个对象上添加了类型注释,编译器会将其视为该类型,即使它可以推断出更具体的对象。如果希望跟踪类型成员而不是子类型,则需要使用类型细化,如val bessy:Animal{type SuitableFood=Grass}
。建议:将标记路径依赖类型添加到此问题。这可能会吸引更了解这种困难的人来回答。(我自己仍在为路径依赖类型而挣扎。)@BenKovitz补充道,谢谢。
val clarabelle: Cow = new Cow
clarabelle eat (new Grass)