2
votes

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

1 Answers

5
votes

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:

  1. The implicit param i gets passed to foo because x would be a forward reference:

    scala> def f(implicit i: Implicit) = {foo; implicit val x = Implicit("i")}
    f: (implicit i: Implicit)Unit
    
  2. Nothing can be passed to foo because parameter i is shadowed by the local value i which 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")}
                                          ^
    
  3. 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)Unit
    
  4. There exists multiple implicits in scope, but one has higher precedence. In this case i has the higher precedence because Null <: Implicit

    scala> 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.