问题
As a follow up to my other question, see comments / questions in code:
case class Implicit(name: String)
def foo(implicit i: Implicit = null) = println(Option(i))
def bar1(implicit i: Implicit) {
foo // prints None
implicit val i = Implicit("J") // Why is call to foo above affected although this line comes after?
foo // prints Some(Implicit(I))
}
def bar2(implicit i: Implicit) {
foo // prints None
implicit val i = null
implicit val j = Implicit("J")
foo // prints None // Why? Should raise ambiguous implicits or at least choose j.
}
def bar3(implicit i: Implicit) {
foo // prints None
val i = null
implicit val j = Implicit("J")
foo // prints Some(Implicit(J)) // Now it works as I expected to work in bar2.
}
def bar4(implicit i: Implicit) { // That's how I expected to see bar1 working. A ugly hack here.
foo // prints Some(Implicit(I))
locally {
val i = null
implicit val j = Implicit("J")
foo // prints Some(Implicit(J))
}
}
val i = Implicit("I")
bar1(i)
bar2(i)
bar3(i)
bar4(i)
回答1:
Your code suffers from name shadowing. The spec in chapter 2 says about this:
A binding has a scope in which the entity defined by a single name can be accessed using a simple name. Scopes are nested. A binding in some inner scope shadows bindings of lower precedence in the same scope as well as bindings of the same or lower precedence in outer scopes.
In your example, this means that with
def foo(implicit i: Implicit) = println(Option(i))
we have the following possibilities:
The implicit param
igets passed tofoobecausexwould be a forward reference:scala> def f(implicit i: Implicit) = {foo; implicit val x = Implicit("i")} f: (implicit i: Implicit)UnitNothing can be passed to
foobecause parameteriis shadowed by the local valueiwhich has higher precedence but can't be called because it is a forward reference.scala> def f(implicit i: Implicit) = {foo; implicit val i = Implicit("i")} <console>:11: error: could not find implicit value for parameter i: Implicit def f(implicit i: Implicit) = {foo; implicit val i = Implicit("i")} ^A value is shadowed when it has the same name, but it must not have the same type:
scala> def f(implicit i: Implicit) = {implicit val j = Implicit("i"); foo} <console>:11: error: ambiguous implicit values: both value i of type Implicit and value j of type Implicit match expected type Implicit def f(implicit i: Implicit) = {implicit val j = Implicit("i"); foo} ^ scala> def f(implicit i: Implicit) = {val i = null; implicit val j = Implicit("i"); foo} f: (implicit i: Implicit)UnitThere exists multiple implicits in scope, but one has higher precedence. In this case
ihas the higher precedence becauseNull <: Implicitscala> def f(implicit i: Implicit) = {implicit val i = null; implicit val j = Implicit("i"); foo} f: (implicit i: Implicit)Unit
Your definition of foo was declared with a default value for the implicit parameter. This doesn't change anything at the rules mentioned above, but the compiler can choose the default value when no other value is available.
来源:https://stackoverflow.com/questions/25096097/scope-of-implicits