【问题标题】:Scala: Implicit parameter resolution precedenceScala:隐式参数解析优先级
【发布时间】:2012-01-27 05:26:28
【问题描述】:

假设我们有只涉及局部范围的隐式参数查找:

trait CanFoo[A] {
  def foos(x: A): String
}

object Def {
  implicit object ImportIntFoo extends CanFoo[Int] {
    def foos(x: Int) = "ImportIntFoo:" + x.toString
  }
}

object Main {
  def test(): String = {
    implicit object LocalIntFoo extends CanFoo[Int] {
      def foos(x: Int) = "LocalIntFoo:" + x.toString
    }
    import Def._

    foo(1)
  }

  def foo[A:CanFoo](x: A): String = implicitly[CanFoo[A]].foos(x)
}

在上面的代码中,LocalIntFoo 胜过ImportedIntFoo。 有人可以使用“静态重载解析规则(第 6.26.3 节)”解释它是如何被认为更具体的吗?

编辑:

名称绑定优先级是一个令人信服的论点,但有几个问题尚未解决。 首先,Scala Language Reference 说:

如果有多个符合条件的参数与隐式参数的类型匹配,则将使用静态重载解析规则(第 6.26.3 节)选择最具体的参数。

其次,名称绑定优先级是将已知标识符 x 解析为特定成员 pkg.A.B.x,以防在范围内有多个名为 x 的变量/方法/对象。 ImportIntFoo 和 LocalIntFoo 的名称不同。

第三,我可以证明名称绑定优先级单独不起作用,如下所示:

trait CanFoo[A] {
  def foos(x: A): String
}

object Def {
  implicit object ImportIntFoo extends CanFoo[Int] {
    def foos(x: Int) = "ImportIntFoo:" + x.toString
  }
}

object Main {
  def test(): String = {
    implicit object LocalAnyFoo extends CanFoo[Any] {
      def foos(x: Any) = "LocalAnyFoo:" + x.toString
    }

    // implicit object LocalIntFoo extends CanFoo[Int] {
    //   def foos(x: Int) = "LocalIntFoo:" + x.toString
    // }
    import Def._

    foo(1)
  }

  def foo[A:CanFoo](x: A): String = implicitly[CanFoo[A]].foos(x)
}

println(Main.test)

将其放入test.scala 并运行scala test.scala,它会打印出ImportIntFoo:1。 这是因为静态重载决议(§6.26.3)说更具体的类型获胜。 如果我们假设所有符合条件的隐式值都被命名为相同,那么LocalAnyFoo 应该屏蔽了ImportIntFoo。

相关:

这是对隐式参数解析的一个很好的总结,但它引用了 Josh 的 nescala 演示而不是规范。他的演讲促使我对此进行研究。

编译器实现

【问题讨论】:

    标签: scala implicit


    【解决方案1】:

    我以博文revisiting implicits without import tax的形式写了自己的答案。

    更新:此外,Martin Odersky 在上述帖子中的 cmets 揭示了 Scala 2.9.1 中 LocalIntFoo 战胜 ImportedIntFoo 的行为实际上是一个错误。见implicit parameter precedence again。

    • 1) 通过本地声明、导入、外部范围、继承、可以不带前缀访问的包对象隐含对当前调用范围可见。
    • 2) 隐式作用域,它包含与我们搜索的隐式类型有某种关系的各种伴随对象和包对象(即类型的包对象,类型的伴随对象本身,它的类型构造函数(如果有的话),它的参数(如果有的话),以及它的超类型和超特征)。

    如果在任一阶段我们发现不止一个隐式的静态重载规则用于解决它。

    更新 2:当我向 Josh 询问有关无进口税的 Implicits 时,他向我解释说他指的是 名称完全相同的implicits 的名称绑定规则 .

    【讨论】:

    • 你真的应该至少在你的答案中添加优先表。
    • 对于那些迷路的人,请点击“再次隐式参数优先级”帖子了解静态重载规则的真正含义。
    【解决方案2】:

    来自http://www.scala-lang.org/docu/files/ScalaReference.pdf,第 2 章:

    Scala 中的名称标识类型、值、方法和类 统称为实体。名称由本地定义引入 和声明(§4),继承(§5.1.3),导入子句(§4.7),或 包子句(第 9.2 节)统称为绑定。

    不同类型的绑定具有定义的优先级: 1. 本地的、继承的或由包子句在同一编译单元中提供的定义和声明 定义发生具有最高优先级。 2. 显式导入的优先级次之。 3. 通配符导入的优先级次之。 4. 不在定义所在的编译单元中的包子句提供的定义具有最低优先级。

    我可能弄错了,但是对 foo(1) 的调用与 LocalIntFoo 在同一个编译单元中,导致转换优先于 ImportedIntFoo。

    【讨论】:

    • 我也注意到了这一点,但是 p。 106 说“如果有几个符合条件的参数与隐式参数的类型匹配,则将使用静态重载解析规则(第 6.26.3 节)选择最具体的参数。”而且它没有提到任何关于绑定优先级的内容。
    • 我认为它与方法解析相同 - 静态一词是指“在编译时”,而不是“在运行时动态”(多次调度)。绑定解析指定所有绑定的范围优先级,包括方法重载,这就是它在这里的应用方式。
    • 这如何解释 ImportIntFoo 与 LocalAnyFoo?查看更新的问题。
    【解决方案3】:

    有人能解释一下如何使用“the 静态重载解析规则 (§6.26.3)"?

    没有方法重载,因此 6.26.3 在这里完全无关紧要。

    重载是指在同一个类上定义了多个同名但参数不同的方法。例如,示例 6.26.1 中的方法 f 被重载:

    class A extends B {}
    def f(x: B, y: B) = . . .
    def f(x: A, y: B) = . . .
    val a: A
    val b: B
    

    隐式参数解析优先级是一个完全不同的规则,它在 Stack Overflow 上已经有了问答。

    【讨论】:

    • Scala 语言参考的第 106 页说,如果有多个候选者,这就是隐式参数的解析方式。你能链接到这个问题吗?如果你说的是stackoverflow.com/questions/6966825,那就是使用 6.26.3,并且没有提到本地与导入。
    • 好的,我确实找到了您的stackoverflow.com/questions/5598085,它指的是 Josh 的 nescala 演讲。演讲是促使我找出在 Scala 语言参考(甚至是 scalac 实现)中引用这些优先级的原因。
    • @EugeneYokota 我明白你的意思,确实不清楚,但我怀疑你引用的文字是指“第二,符合条件的也是属于隐式范围的某个对象的所有隐式成员隐式参数的类型, T 。”特别是,由于此文本已经确定了优先级(尽管不够具体,无法回答您的问题)。
    猜你喜欢
    • 1970-01-01
    • 2021-09-03
    • 1970-01-01
    • 2020-07-06
    • 1970-01-01
    • 2013-12-27
    • 1970-01-01
    • 1970-01-01
    • 2012-07-17
    相关资源
    最近更新 更多