2015-08-22 121 views
5

我正在讀Scala中的書編程的部分20.7,我想知道爲什麼儘管該代碼編譯:Scala的類型:A類不等於,其中T爲T:類型T = A

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 eat (new Grass) 

而且據我瞭解草的類型是一樣的Cow.SuitableFood的。

另外,我有一個關於這個例子另一個問題:

如果BESSY的類型是動物,怎麼能編譯器知道它需要一個類型SuitableFood - >草,而不是一個類型的食物? 「因爲試圖提供一種新的食物給我的類型不匹配的編譯錯誤,但是類動物需要食品和BESSY的類型是明確定義:動物

+0

建議:將標記_path-dependent-type_添加到此問題。這可能會吸引更多地瞭解這種困難的人的答案。 (我仍然在努力與路徑依賴類型本身。) –

+0

@ BenKovitz補充,謝謝。 – vicaba

回答

10

這是因爲bessie聲明Animal而非Cowbessie.SuitableFood是一個「路徑依賴類型」(見下文)。

試試這個:

val clarabelle: Cow = new Cow 

clarabelle eat (new Grass) 

這工作,因爲編譯器可以推斷clarabelle.SuitableFood = Grassclarabelle的聲明的類型。

由於bessie聲明Animal,不Cow,編譯器不能安全地推斷,bessie.SuitableFood = Grass *當你說new bessie.SuitableFood,編譯器生成的代碼來看看實際bessie對象,並生成相應類型的新實例。 bessie.SuitableFood是一個「路徑依賴類型」:導致最後一個標識符(SuitableFood)的「path」(bessie.部分)實際上是該類型的一部分。這使您可以爲同一個類的每個單獨對象創建一個類型的自定義版本。


* 嗯,其實,我認爲,如果編譯器是一個有點小聰明,它可以推斷bessie.SuitableFood = Grass,因爲bessieval,不是var,因此不會改變它的類型。換句話說,編譯器應該知道,即使bessie被聲明爲Animal,她確實是Cow。也許未來版本的編譯器會利用這些知識,也許這是一個很好的理由,爲什麼這不是一個好主意,哪個人比我會告訴我們的更專家。 (後記:剛剛做了!見下面的Travis Brown的評論。)

+7

關於你的腳註:如果你在類型上加了一個類型註解,編譯器會將它視爲該類型,即使它能夠推斷出更具體的內容。如果你想跟蹤類型成員而不是子類型,你需要使用'val bessy:Animal {type SuitableFood = Grass}'類型的細化。 –

1

關於你的問題的第二部分:它不。 Animal並未指定其食物爲Food,但某些亞型爲Food。編譯器是否會接受這一點,像你的例子那樣的代碼會編譯,錯誤的是。編譯器不知道必要的子類型是Grass(這也是爲什麼eat(new Grass)不起作用的原因),它只知道你的母牛不能吃一些食物,並對此謹慎。

1

我相信bessy eat (new bessy.SuitableFood)編譯是一個錯誤(在2.11中修復)。因爲Animal的另一個亞型可能具有SuitableFood,其中new是沒有意義的,例如, type SuitableFood = Food或甚至type SuitableFood = Food with IntFood with IntFood的完美的子類型!)。

相關問題