2014-10-01 65 views
3

比方說我們想用類型類來實現漂亮的印刷:斯卡拉:implicits,子類和成員類型

trait Printer[T] {def print(t: T)} 

與整數默認實現:我們要

implicit object IntPrinter extends Printer[Int] { 
    override def print(i : Int): Unit = println(i) 
} 

我們的具體類型打印如下:

trait Foo { 
    type K 
    val k: K 
} 

class IntFoo extends Foo { 
    override type K = Int 
    override val k = 123 
} 

很酷。現在我想建立所有FOOS打印機可打印Ks的

implicit def fooPrinter[FP <: Foo](implicit ev: Printer[FP#K]): Printer[FP] = 
    new Printer[FP] { 
     override def print(f: FP): Unit = { 
      Predef.print("Foo: ") 
      ev.print(f.k) 
     } 
    } 

讓檢查implicits得到解決:

def main(args: Array[String]) { 
    implicitly[Printer[Int]] 
    implicitly[Printer[IntFoo]] 
} 

scalac 2.11.2說:

diverging implicit expansion for type Sandbox.Printer[Int] 
    starting with method fooPrinter in object Sandbox 
     implicitly[Printer[Int]] 

whaat?

OK,讓我們重寫fooPrinter:

implicit def fooPrinter[KP, FP <: Foo {type K = KP}](implicit ev: Printer[KP]) = 
    new Printer[FP] { 
     override def print(f: FP): Unit = { 
      Predef.print("Foo: ") 
      ev.print(f.k) 
     } 
    } 

這部作品在2.11,但什麼是與第一種方法的問題? 不幸的是,我們在2.10,第二個解決方案仍然無法正常工作。它編譯直到我們添加像

implicit object StringPrinter extends Printer[String] { 
    override def print(s : String): Unit = println(s) 
} 

一個更森達打印機,它神祕地打破打印機[IntFoo]隱:

could not find implicit value for parameter e: 
    Sandbox.Printer[Sandbox.IntFoo] 

編譯器錯誤?

回答

2

隱式聲明的順序很重要。在源代碼重新排序原代碼

implicit object IntPrinter ... 
... 
implicit def fooPrinter ... 

implicit def fooPrinter ... 
... 
implicit object IntPrinter ... 
+0

移動它修復fooPrinter對2.11的第一個定義(並導致對2.10真是奇怪的錯誤)。如果訂單不同,他們爲什麼會衝突? – eprst 2014-10-02 18:09:56

+0

我對隱含的看法是,如果甚至有一點疑問,那麼它根本不起作用。它是由設計偏執狂,以便它不會意外解決你可能不期望的事情......但是這並不能回答你的問題。我會假設編譯器已經發現了一個隱含的定義Printer [FP],它可能僅僅通過查看它的返回值就可以返回Printer [Int]。此時它可能不檢查FP <:Foo限制。然後它爲Printer [Int]找到另一個隱式定義。這就是錯誤。但那只是我的理論。 – 2014-10-02 18:24:22

+0

隱式分辨率如此脆弱的另一個原因是編譯速度。正如你可以想象的那樣,如果它真的試圖評估所有可能的隱式定義,它可能會成爲指數回溯惡夢。但透明度可能更重要。 – 2014-10-02 18:28:28