2012-04-27 35 views
6

這個問題出現在我正在編寫的模塊中,但我做了一個展示相同行爲的最小案例。爲什麼不在這裏輸入推理?

class Minimal[T](x : T) { 
    def doSomething = x 
} 

object Sugar { 
    type S[T] = { def doSomething : T } 
    def apply[T, X <: S[T]] (x: X) = x.doSomething 
} 

object Error { 
    val a = new Minimal(4) 
    Sugar(a) // error: inferred [Nothing, Minimal[Int]] does not fit the bounds of apply 
    Sugar[Int, Minimal[Int]](a) // works as expected 
} 

的問題是,編譯器設法計算出用於MinimalInt)內的參數,但然後設置的T其它發生於Nothing,這顯然不匹配apply。這些肯定是相同的T,因爲刪除第一個參數使第二個抱怨T未定義。

是否存在一些含糊不清的含義,即編譯器無法推斷出第一個參數,或者這是一個錯誤?我能優雅地解決這個問題嗎?

更多信息:此代碼是一個嘗試語法糖的簡單示例。原始代碼試圖使|(a)|表示模數a,其中a是一個向量。很明顯,|(a)|比編寫|[Float,Vector3[Float]](a)|要好,但不幸的是我不能使用unary_|來使這更容易。

實際的錯誤:

inferred type arguments [Nothing,Minimal[Int]] do not conform to method apply's type parameter bounds [T,X <: Sugar.S[T]]

回答

9

這不是一個Scala編譯器錯誤,但它當然是Scala類型推斷的限制。編譯器想要在求解X之前確定X,上的邊界,但是邊界提到了迄今爲​​止無約束的類型變量T,因此它在Nothing處修正並從那裏繼續。一旦X已完全解決,它不會重新訪問T ...目前,在這種情況下,類型推斷總是從左到右繼續。

如果您的示例準確代表你的真實情況然後有一個簡單的解決,

def apply[T](x : S[T]) = x.doSomething 

這裏T將推斷使得Minimal符合S[T]直接而不是通過中間有界類型的變量。

更新

約書亞的解決方案也避免了類型推斷的T的問題,但在一個完全不同的方式。

def apply[T, X <% S[T]](x : X) = x.doSomething 

desugars到,

def apply[T, X](x : X)(implicit conv : X => S[T]) = x.doSomething 

的類型變量TX現在可以解決的,用於獨立地(因爲TX的結合不再提及)。這意味着X被立即推斷爲Minimal,並且T被解決爲隱式搜索X => S[T]類型的值以滿足隱式參數conv的一部分。 conformsscala.Predef製造這種形式的值,並在上下文將保證給定的參數類型Minimal,T將被推斷爲詮釋。您可以將其視爲在斯卡拉工作的functional dependencies的實例。

+0

是的,這個解決方案在我的情況下工作正常。它比約書亞的解決方案更清潔(對不起約書亞!)。你能解釋爲什麼約書亞的解決方案有效嗎? – Dylan 2012-04-27 18:37:43

+0

答案更新,以解釋Joshua解決方案的原因。 – 2012-04-27 20:05:49

4

有一些古怪與結構類型界限,嘗試使用結合在S [T]代替的圖。

def apply[T, X <% S[T]] (x: X) = x.doSomething工作正常。

+1

偉大的,這個工程,但肯定應該沒有區別的方法?在這種情況下采取一種觀點只是投向一個超類,不是嗎?這是否意味着此修復只是解決編譯器中的錯誤的一種解決方法? – Dylan 2012-04-27 01:22:53

+0

啊,現在我明白了,'S'不是超類 - 它只是一種觀點(從某種意義上說)。因此,儘管在大多數情況下並不需要,但觀點更合適。 – Dylan 2012-04-27 18:09:36