【问题标题】:Scala: implicits, subclassing and member typesScala:隐式、子类化和成员类型
【发布时间】:2014-10-02 14:15:18
【问题描述】:

假设我们想使用类型类来实现漂亮的打印:

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

int 的默认实现:

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
}

酷。现在我想用可打印的 Ks 为所有 Foos 构建打印机

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)
        }
    }

让我们检查隐式是否已解决:

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]]

什么?

好的,让我们重写 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 上,第二个解决方案仍然不起作用。它会一直编译,直到我们再添加一台 sim 打印机,例如

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

它神秘地破坏了 Printer[IntFoo] 隐式:

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

编译器错误?

【问题讨论】:

    标签: scala


    【解决方案1】:

    隐式声明的顺序很重要。在您的源代码中重新排序原始代码来自

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

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

    【讨论】:

    • 移动它修复了 fooPrinter 在 2.11 上的第一个定义(并在 2.10 上导致非常奇怪的错误)。如果顺序不同,它们为什么会发生冲突的任何解释?
    • 我对隐式的看法是,即使有一点怀疑,它也根本行不通。它是设计偏执的,因此它不会意外地解决您可能没想到的事情……但这并不能回答您的问题。我假设编译器已经找到了一个隐式定义 Printer[FP],它可能仅通过查看它的返回值来返回 Printer[Int]。此时它可能不会检查 FP <: foo printer>
    • 隐式解析如此脆弱的另一个原因也是编译速度。正如你可以想象的那样,如果它真的试图评估所有可能的隐式定义,它可能会成为一个指数回溯的噩梦。但透明度可能更重要。
    • 如果我错了,请纠正我,但应该是线性的,因为编译器没有尝试所有隐含的组合。是的,我认为正确性和透明度应该优先于编译速度:)
    • 你可能是对的,这取决于算法的聪明程度。但无论如何,编译器肯定不会真正尝试所有可能的组合。因此,在某些情况下,隐含解析“应该”仅通过查看代码就可以工作,但这并不是因为那些偏执的规则。
    猜你喜欢
    • 2018-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-28
    • 1970-01-01
    • 2017-01-30
    • 2011-08-29
    • 1970-01-01
    相关资源
    最近更新 更多