假设我们只有局部范围的隐式参数查找:
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
的变量/方法/对象。 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演示而不是规范。他的演讲是我调查此事的动力。
编译器实施
答案 0 :(得分:16)
我以博文revisiting implicits without import tax的形式写了我自己的答案。
更新:此外,Martin Odersky在上述帖子中的评论显示,Scala 2.9.1 LocalIntFoo
赢得ImportedIntFoo
的行为实际上是一个错误。请参阅implicit parameter precedence again。
如果在任一阶段我们发现多个隐式静态重载规则用于解决它。
更新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上有一个问题和答案。