2012-09-26 65 views
17

最近我偶然發現了一個奇怪的(對我)編譯器錯誤消息。請看下面的代碼:A類有一個類型參數,但B類有一個

trait Foo { 
    type Res <: Foo 
    type Bar[X <: Res] 
} 

class MyFoo extends Foo { 
    override type Res = MyFoo 
    override type Bar[X <: Res] = List[X] 
} 

type FOO[F <: Foo, R <: Foo, B[_ <: R]] = F { type Res = R; 
               type Bar[X <: R] = B[X] } 

def process[F <: Foo, R <: Foo, B[_ <: R]](f: FOO[F, R, B]) {} 

現在,如果我要撥打的process方法我必須明確地寫出類型參數:

process[MyFoo, MyFoo, List](new MyFoo) // fine 

如果我寫:

process(new MyFoo) 

process((new MyFoo): FOO[MyFoo, MyFoo, List]) 

我收到以下錯誤消息:

推斷類型參數(MyFoo,MyFoo,List [X])的種類不符合預期種類的參數(類型F,類型R,類型B )。列表[X]的類型參數不匹配B型的預期參數:類List有一種類型的參數,但B型有一個

爲什麼isn't編譯器能夠推斷出類型(雖然我明確說明他們在呼叫參數)?那class List has one type parameter, but type B has one是什麼意思?東西有一個,但另一個也有之一,這就是爲什麼他們不適合在一起?

+0

我正在使用scala 2.9.3-20120917-121530-db16547873 –

回答

2

給出。如果我們着眼於特定類型兼容Scala編譯器,來源可以幫助我們理解問題所在。我從來沒有對Scala編譯器做過貢獻,但我發現這些源代碼非常易讀,而且我已經對此進行了研究。

負責類型推斷的類是scala.tools.nsctypechecker.Infer,您可以通過在Scala編譯器源中查找錯誤的一部分來簡單地找到它。你會發現下面的代碼片段:

/** error if arguments not within bounds. */ 
    def checkBounds(pos: Position, pre: Type, owner: Symbol, 
        tparams: List[Symbol], targs: List[Type], prefix: String) = { 
     //@M validate variances & bounds of targs wrt variances & bounds of tparams 
     //@M TODO: better place to check this? 
     //@M TODO: errors for getters & setters are reported separately 
     val kindErrors = checkKindBounds(tparams, targs, pre, owner) 

     if(!kindErrors.isEmpty) { 
     error(pos, 
      prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") + 
      " do not conform to the expected kinds of the type parameters "+ tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." + 
      kindErrors.toList.mkString("\n", ", ", "")) 
     } 

所以,現在的點是理解爲什麼checkKindBounds(tparams, targs, pre, owner)返回這些錯誤。如果你走在方法調用鏈,你會看到checkKindBounds調用另一個方法

val errors = checkKindBounds0(tparams, targs, pre, owner, true) 

你會看到這個問題被連接到檢查更高kinded類型的界限,在線路5784,裏面checkKindBoundsHK:

if (!sameLength(hkargs, hkparams)) { 
     if (arg == AnyClass || arg == NothingClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded 
     else {error = true; (List((arg, param)), Nil, Nil) } // shortcut: always set error, whether explainTypesOrNot 
     } 

測試不通過,它出現在我的調試器:

hkargs$1 = {[email protected]}"List()" 
arg$1 = {[email protected]}"class List" 
param$1 = {[email protected]}"type B" 
paramowner$1 = {[email protected]}"method process" 
underHKParams$1 = {[email protected]}"List(type R)" 
withHKArgs$1 = {[email protected]}"List()" 
exceptionResult12 = null 
hkparams$1 = {[email protected]}"List(type R)" 

所以它看起來像有一個更高的kinded PARAM,R型,但沒有提供d值。

如果你真的回去給checkKindBounds,你看到片段後:

val (arityMismatches, varianceMismatches, stricterBounds) = (
     // NOTE: *not* targ.typeSymbol, which normalizes 
     checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO) 
    ) 

arityMismatches包含一個元組列表,B.現在你還可以看到錯誤消息是錯誤的:

推斷種類型參數(MyFoo,MyFoo,列表[X])不 符合預期的種類型的參數(F型,型 R,B型)的。列表[X]的類型參數不匹配B型的預期 參數:類List有一種類型的參數,但B型有ZERO

事實上,如果你在下面放一個斷點行5859調用

checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO) 

你可以看到,

tparam = {[email protected]}"type B" 
targ = {[email protected]}"List[X]" 

結論:

由於某些原因,在處理諸如您的複雜高級類型時,Scala編譯器推斷受到限制。我不知道它來自哪裏,也許你想發送一個bug到編譯器團隊

0

我只對Scala中類型推理器的確切工作有一個模糊的理解,所以認爲這個想法不是確定性答案。

  1. 類型推斷有一次推斷多個類型的問題。

  2. 您在FOO的定義,轉化爲使用一個存在的類型:存在這樣的類型,不知道這是在MyFoo

相關問題