Scala:隐式参数解析优先级

时间:2011-12-24 06:13:01

标签: scala implicit

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

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语言参考说:

  

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

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

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

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演示而不是规范。他的演讲是我调查此事的动力。

编译器实施

3 个答案:

答案 0 :(得分:16)

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

更新:此外,Martin Odersky在上述帖子中的评论显示,Scala 2.9.1 LocalIntFoo赢得ImportedIntFoo的行为实际上是一个错误。请参阅implicit parameter precedence again

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

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

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

答案 1 :(得分:7)

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

  

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

     

不同类型的绑定优先于它们定义:   1.由同一编译单元中的包子句本地,继承或提供的定义和声明   定义发生具有最高优先权。   2.明确的进口具有次高的优先权。   3.通配符导入的优先级次高。   4.由一个不在编码单元中的包子句提供的定义具有最低优先级。

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

答案 2 :(得分:1)

  

有人可以解释一下如何使用“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上有一个问题和答案。