2015-06-08 38 views
1

我正在通過從「Scala for the Impatient」一書中學習Scala來學習Scala。有一個問題問:Scala:使用隱式證據的通用方法不能編譯

給出一個可變Pair[S, T]類,使用類型約束來定義 交換方法中,可以在類型參數是相同的調用。

我的代碼:

class Pair[T, S](var first: T, var second: S) { 
    def swap[T, S](implicit ev: T =:= S) { 
    val temp = first 
    first = second // doesn't compile 
    second = temp 
    } 
} 

上述代碼失敗,並抱怨firstsecond是不同類型的編譯。那麼,我很好地告訴編譯器他們不是。我怎麼能告訴它閉嘴?

回答

7

你剛剛告訴類型傳遞給你的類爲TS應該是平等的編譯器 - 你只需要他們的平等,它可以被用來推斷實際的證據TS正確,當你經過實際類型(但不在泛型類本身內)。這並不意味着TS是可互換的。順便說一句,它不會改變任何東西,但你通過定義新ST做了一個錯誤,應該是:

class Pair[T, S](var first: T, var second: S) { 
    def swap(implicit ev: T =:= S) { //without [S, T] - they are unrelated to original ones, it's whole new names 
     val temp = first 
     first = second // doesn't compile 
     second = temp 
    } 
    } 

但是它仍然不能編譯。爲什麼?試想一下:

def getBoth(implicit ev: T =:= S) = List(first, second) 

那麼什麼是返回類型編譯器應該推斷? List[T]List[S]。唯一可以做的是List[Any]。所以相同和不同類型的模擬沒有任何意義。如果你想要不同的名字 - 只需使用type S = T就不需要證據。

什麼是ST不同的構造方法和在某些方法相同的真實情況。這在邏輯上是不正確的(當談論抽象類型時 - 不是具體的替換)。

Btw,=:=不是編譯器功能 - 在編譯器中沒有特殊處理。整個實施是它裏面斯卡拉Predef

@implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.") 
    sealed abstract class =:=[From, To] extends (From => To) with Serializable 
    private[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x } 
    object =:= { 
    implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A] 
    } 

所以它只是tpEquals[A]隱含這需要鍵入A,讓您A =:= A - 當你需要T =:= U它試圖做這樣的轉換,只有同等類型是可能的。只有當你通過實際的TU,而不是當你定義它們時,檢查隱式本身的過程實際上只發生。

關於你的具體問題:

class Pair[T, S](var first: T, var second: S) { 
    def swap(implicit ev: T =:= S) { 
     val temp = first 
     first = second.asInstanceOf[T] 
     second = temp.asInstanceOf[S] 
    } 
} 

scala> new Pair(5,6) 
res9: Pair[Int,Int] = [email protected] 

scala> res9.swap 

scala> res9.first 
res11: Int = 6 

或者只是(如@mz和@Imm建議):

class Pair[T, S](var first: T, var second: S) { 
    def swap(implicit ev: T =:= S, ev2: S =:= T) { 
     val temp = first 
     first = second 
     second = temp 
    } 
} 

T =:= S擴展T => S這隱含地添加功能(甚至作爲一個對象)解釋爲在Scala中從TS的隱式轉換,所以它適用於兩種類型是相同的,這非常酷。

+0

你怎麼解決給定的問題呢?從問題陳述「給定可變對[S,T]類...」,我猜測作者不希望我在類級別上定義隱式證據,但僅在方法級別上定義。 –

+0

你已經解決了這個問題 - 只要做'asInstanceOf [S]','asInstanceOf [T]'。我只是說,編譯器不能爲你自動完成,一般情況下 – dk14

+0

謝謝你,我已經接受你的答案,也贊成它。 –