Scala:隐式参数解析优先级

Eug*_*ota 15 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)
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,LocalIntFoo胜过ImportedIntFoo.有人可以使用"静态重载决策规则(§6.26.3)"来解释它是如何被认为更具体的吗?

编辑:

名称绑定优先级是一个引人注目的参数,但有几个问题尚未解决.首先,Scala语言参考说:

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

其次,名称绑定优先级是关于在范围内有多个变量/方法/对象的情况下解析x特定成员的已知标识符.并且没有相同的名称.pkg.A.B.xxImportIntFooLocalIntFoo

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

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)
Run Code Online (Sandbox Code Playgroud)

把它放入test.scala并运行scala test.scala,然后打印出来ImportIntFoo:1.这是因为静态重载决策(第6.26.3节)表示更具体的类型获胜.如果我们假装所有符合条件的隐式值都被命名为相同,则LocalAnyFoo应该进行掩码ImportIntFoo.

相关:

这是隐式参数解析的一个很好的总结,但它引用了Josh的nescala表示而不是规范.他的演讲是我调查这一点的动机.

编译器实现

Eug*_*ota 16

我以博客文章的形式写了我自己的答案,重新审视了没有进口税的含义.

更新:此外,Martin Odersky在上述帖子中的评论显示,Scala 2.9.1的LocalIntFoo获胜行为ImportedIntFoo实际上是一个错误.请再次参见隐式参数优先级.

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

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

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

  • 您应该至少在答案中添加优先级表. (3认同)

Chr*_*ain 7

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

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

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

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