在Scala2.13中,为什么伴随对象的隐式作用域有时可能会错位?如何纠正?
下面是一个测试 scala 2.13 编译器隐式特性的简单示例:
object OverridingScope {
trait System {
trait Handler[T]
}
object Sys1 extends System {
object Handler {
implicit def handle1: Handler[Int] = new Handler[Int] {}
}
implicit def handle2: Handler[Long] = new Handler[Long] {}
}
}
根据这篇文章:
Scala 在哪里寻找隐式?
Handler当使用 type 时,trait 的伴生对象可以作为隐式作用域的一部分自动导入Handler[_],外部对象Sys1应该是无关紧要的。这意味着 handle1 应该是可见的,而 handle2 应该是隐藏的。
但是下面的测试用例显示了完全相反的情况:
class OverridingScope extends BaseSpec {
import OverridingScope._
it("implicits in companion is in scope") {
// val hh = implicitly[Sys1.Handler[Int]]
// Doesn't work
}
it("implicits in outer object is in scope") {
val hh = implicitly[Sys1.Handler[Long]]
// Should NOT work
}
}
考虑到 Handler 既是类名又是对象名,handle1 不起作用的事实看起来非常可疑。另外,我检查了隐式导入的完整列表:
First look in current scope
Implicits defined in current scope
Explicit imports
wildcard imports
Same scope in other files
Now look at associated types in
Companion objects of a type
Implicit scope of an argument's type (2.9.1)
Implicit scope of type arguments (2.8.0)
Outer objects for nested types
Other dimensions
以上都不能解释为什么 handle2 在范围内。可能是什么原因?
顺便说一句:对于那些认为隐式是一个重要的 Scala 特性的人:隐式是实现类型类的唯一方法,如果你想用最少的样板来实现它,有时上面的模式可能是唯一的选择。
回答
这里的第 3 点解释了所讨论类型的前缀中的隐式/给定有助于其隐式范围(在 Scala 3 中不是如果它是一个包)。所以对于类型Sys1.Handler[Long]隐含在Sys1隐式范围内,因为Sys1是一个非包前缀。
handle1不在隐式范围内,因为 objectHandler中的 objectSys1不是 traitHandler中 trait的伴生对象System。
- Yes but the only thing that should be different is the rule that the prefix can't be a package in Scala 3.
THE END
二维码