2015-08-30 9 views
0

我一直在試驗隱式轉換,並且對使用這些轉換的'enrich-my-libray'模式有一個體面的理解。我想我的基本implicits的理解與運用隱含的證據結合起來......但我誤解的東西重要,如通過下面的方法:我不清楚爲什麼我的範圍內隱式轉換不被接受爲'隱式證據'

import scala.language.implicitConversions 

object Moo extends App { 

    case class FooInt(i: Int) 
    implicit def cvtInt(i: Int) : FooInt = FooInt(i) 
    implicit def cvtFoo(f: FooInt) : Int = f.i 

    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 
    } 

    def dump() = { 
     println("first is " + first) 
     println("second is " + second) 
    } 
    } 

    val x = new Pair(FooInt(200), 100) 
    x.dump 
    x.swap 
    x.dump 
} 

當我運行上面的方法我得到這個錯誤:

Error:(31, 5) Cannot prove that nodescala.Moo.FooInt =:= Int. 
     x.swap 
     ^

我百思不得其解,因爲我本來以爲我在範圍內隱式的轉換將是足夠的「證據」是詮釋的可轉換爲FooInt的,反之亦然。在此先感謝您爲我設置這個!

UPDATE:

由彼得的出色答卷被unconfused後,下面的燈泡持續了我,你會想使用隱證據的API中的一個很好的理由。我詳細說明了我自己對這個問題的回答(也在下面)。

回答

5

=:=檢查兩種類型是否相等,並且FooIntInt確實不相等,儘管這兩種類型的值存在隱式轉換。

我會創造一個CanConvert型類可以將A轉換成B

trait CanConvert[A, B] { 
    def convert(a: A): B 
} 

我們可以創建類型的類的實例轉換IntFooInt,反之亦然:

implicit val Int2FooInt = new CanConvert[Int, FooInt] { 
    def convert(i: Int) = FooInt(i) 
} 

implicit val FooInt2Int = new CanConvert[FooInt, Int] { 
    def convert(f: FooInt) = f.i 
} 

現在我們可以在我們的Pair.swap功能中使用CanConvert

class Pair[A, B](var a: A, var b: B) { 
    def swap(implicit a2b: CanConvert[A, B], b2a: CanConvert[B, A]) { 
    val temp = a 
    a = b2a.convert(b) 
    b = a2b.convert(temp) 
    } 

    override def toString = s"($a, $b)" 

    def dump(): Unit = println(this) 
} 

,我們可以使用如下:

scala> val x = new Pair(FooInt(200), 100) 
x: Pair[FooInt,Int] = (FooInt(200), 100) 

scala> x.swap 

scala> x.dump 
(FooInt(100), 200) 
+1

這不僅回答了問題,而且更清晰的代碼更易於維護。這是一個加號:) –

+0

謝謝,彼得。既然你的答案也提供了一個很好的類型類的簡潔例子,我添加了typeclasses標籤到我原來的問題。 –

3

A =:= B的證據表明,可以轉化爲B.事實是,A可以cast到B.你有沒有隱含的證據在任何地方Int可以轉換爲FooInt,反之亦然(出於很好的理由;)。

你所尋找的是:

def swap(implicit ev: T => S, ev2: S => T) { 
0

通過本練習中,參加工作後,我認爲我有一個更好的瞭解爲什麼你想使用隱式的證據提供您的API中。

隱證據可以是非常有用的時候:

  • 你有一個類型參數化類,提供了由參數給出的類型的行爲的各種方法 和
  • 當一個或多個的那些方法只有在附加的 約束條件放在參數化類型上時纔有意義。

所以,在我原來的問題給出的簡單API的情況下:

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

我們有一種對,這使兩者結合起來,我們可以隨時調用轉儲()檢查這兩件事。在一定條件下,我們還可以交換一對中第一項和第二項的位置。這些條件是由隱含的證據約束給出的。


斯卡拉書中的編程給a nice example of how this technique is used in Scala collections,特別是在Traversables的toMap方法。

書中指出,地圖的構造

wants key-value pairs, i.e., two-tuples, as arguments. If we have a sequence [Traversable] of pairs, wouldn’t it be nice to create a Map out of them in one step? That’s what toMap does, but we have a dilemma. We can’t allow the user to call toMap if the sequence is not a sequence of pairs.

所以這是一個類型[Traversable的],有一個方法[toMap]不能在所有情況下使用的例子...它只能當編譯器可以'證明'(通過隱含證據)Traversable中的項是成對的時候使用。

相關問題