2017-02-11 87 views
3

簡單的例子來說明該問題:爲什麼scala未能對f-綁定多態性進行類型推斷?

trait WTF[W <: WTF[W]] { 
    def get : Int 
} 

trait Zero extends WTF[Zero] { 
    override def get : Int = 0 
} 
case object Zero extends Zero 

final case class Box(inner : Int) extends WTF[Box] { 
    override def get : Int = inner 
} 

def printWTF[W <: WTF[W]](w : W) = println(w.get) 

printWTF(Box(-1)) 
printWTF(Zero) 

Box是好的,但Zero產生錯誤:

WTF.scala:22: error: inferred type arguments [Zero.type] do not conform to method printWTF's type parameter bounds [W <: WTF[W]] 
    printWTF(Zero) 
^
WTF.scala:22: error: type mismatch; 
found : Zero.type 
required: W 
    printWTF(Zero) 
     ^
two errors found 

如果我手動註釋的類型,它編譯:

printWTF[Zero](Zero) 
printWTF(Zero : Zero) 

第一線路按預期工作。我經常遇到無法從參數中推斷出類型參數的情況。例如def test[A](x : Int) : UnitA類型在參數簽名中無處顯示,因此應該手動指定它。

但是後者對我來說很模糊。我只是添加了總是爲真的類型轉換,奇蹟般地編譯器學習如何推斷方法類型參數。但Zero始終是Zero類型,爲什麼編譯器無法從我的提示中推斷出它?

回答

2

案件對象Zero的類型爲Zero.type,並且是WTF[Zero]的子類型。因此,當您撥打printWTF(Zero)時,編譯器推斷出W = Zero.type,但Zero.type <: WTF[Zero.type]爲false,因此編譯失敗。

這在另一方面更復雜的簽名應該工作:

def printWTF[W <: WTF[W], V <: W](w: V with WTF[W]) = println(w.get) 

而且作爲示範,這確實是正確的推斷類型:

scala> def printWTF[W <: WTF[W], V <: W](w: V with WTF[W]): (V, W) = ??? 
printWTF: [W <: WTF[W], V <: W](w: V with WTF[W])(V, W) 

scala> :type printWTF(Box(1)) 
(Box, Box) 

scala> :type printWTF(Zero) 
(Zero.type, Zero) 
+0

>案例對象零的類型爲Zero.type和是WTF的子類型[0] 但是一般規則是你可以用任何子類替換任何類入口。第二行示例完全一樣。它不會更改類型簽名。 – ayvango

+0

我不認爲任何人都知道Scala中完整的類型推斷規則。我所知道的是,它不會在您的示例中重試所有超類型的'Zero.type',並且您必須像我一樣使用一些額外的參數來幫助它。如果你的意思是你可以用'Zero.type'替換'WTF [Zero]中的'Zero',那麼你錯了,因爲'WTF'中的'W'是不變的。 –

+0

我並不是說'WTF [Zero]裏面的'Zero'可以替代。但任何地方,凡是預期的'Zero'參數都可以用'Zero.type'替代,因爲函數調用允許這樣做,'Function [Zero,T]'是Function [Zero.type,T]的子類型' – ayvango