![](/img/trans.png)
[英]Type bounds unexpectedly change precedence of Scala implicit parameter resolution
[英]Scala: Implicit parameter resolution precedence
假设我们只有局部范围的隐式参数查找:
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.ABx
,以防在作用pkg.ABx
有多个名为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表示而不是规范。 他的演讲是我调查这一点的动机。
编译器实现
我以博客文章的形式写了我自己的答案, 重新审视了没有进口税的含义 。
更新 :此外,Martin Odersky在上述帖子中的评论显示,Scala 2.9.1的LocalIntFoo
赢得ImportedIntFoo
的行为实际上是一个错误。 请再次参见隐式参数优先级 。
如果在任一阶段我们发现多个隐式,则使用静态重载规则来解决它。
更新2 :当我向Josh询问没有进口税的Implicits时,他向我解释说他指的是名称绑定规则的名称完全相同的implicits 。
来自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。
有人可以使用“静态重载决策规则(§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上有一个问题和答案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.