17

Suppose we have implicit parameter lookup concerning only local scopes:

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)
}

In the above code, LocalIntFoo wins over ImportedIntFoo. Could someone explain how it's considered more specific using "the rules of static overloading resolution (§6.26.3)"?

Edit:

The name binding precedence is a compelling argument, but there are several issues unresolved. First, Scala Language Reference says:

If there are several eligible arguments which match the implicit parameter’s type, a most specific one will be chosen using the rules of static overloading resolution (§6.26.3).

Second, name binding precedence is about resolving a known identifier x to a particular member pkg.A.B.x in case there are several variable/method/object named x in the scope. ImportIntFoo and LocalIntFoo are not named the same.

Third, I can show that name binding precedence alone is not in play as follows:

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)

Put this in test.scala and run scala test.scala, and it prints out ImportIntFoo:1. This is because static overloading resolution (§6.26.3) says more specific type wins. If we are pretending that all eligible implicit values are named the same, LocalAnyFoo should have masked ImportIntFoo.

Related:

This is a great summary of implicit parameter resolution, but it quotes Josh's nescala presentation instead of the spec. His talk is what motivated me to look into this.

Compiler Implementation

Community
  • 1
  • 1
Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319

3 Answers3

17

I wrote my own answer in the form of a blog post revisiting implicits without import tax.

Update: Furthermore, the comments from Martin Odersky in the above post revealed that the Scala 2.9.1's behavior of LocalIntFoo winning over ImportedIntFoo is in fact a bug. See implicit parameter precedence again.

  • 1) implicits visible to current invocation scope via local declaration, imports, outer scope, inheritance, package object that are accessible without prefix.
  • 2) implicit scope, which contains all sort of companion objects and package object that bear some relation to the implicit's type which we search for (i.e. package object of the type, companion object of the type itself, of its type constructor if any, of its parameters if any, and also of its supertype and supertraits).

If at either stage we find more than one implicit, static overloading rule is used to resolve it.

Update 2: When I asked Josh about Implicits without Import Tax, he explained to me that he was referring to name binding rules for implicits that are named exactly the same.

Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
7

From http://www.scala-lang.org/docu/files/ScalaReference.pdf, Chapter 2:

Names in Scala identify types, values, methods, and classes which are collectively called entities. Names are introduced by local definitions and declarations (§4), inheritance (§5.1.3), import clauses (§4.7), or package clauses (§9.2) which are collectively called bindings.

Bindings of different kinds have a precedence defined on them: 1. Definitions and declarations that are local, inherited, or made available by a package clause in the same compilation unit where the definition occurs have highest precedence. 2. Explicit imports have next highest precedence. 3. Wildcard imports have next highest precedence. 4. Definitions made available by a package clause not in the compilation unit where the definition occurs have lowest precedence.

I may be mistaken, but the call to foo(1) is in the same compilation unit as LocalIntFoo, resulting in that conversion taking precedence over ImportedIntFoo.

Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • 1
    I've noticed that too, but p. 106 says "If there are several eligible arguments which match the implicit parameter’s type, a most specific one will be chosen using the rules of static overloading resolution (§6.26.3)." And it doesn't mention anything about binding precedence. – Eugene Yokota Dec 24 '11 at 06:47
  • I think that it's just the same as method resolution- the word static is in reference to "at compile time", as opposed to "dynamically at runtime" (multiple dispatch). Binding resolution specifies the scope precedence for all bindings, including method overloads, which is how it applies here. – Chris Shain Dec 24 '11 at 16:34
  • How does that explain `ImportIntFoo` vs `LocalAnyFoo`? See updated question. – Eugene Yokota Dec 24 '11 at 19:19
1

Could someone explain how it's considered more specific using "the rules of static overloading resolution (§6.26.3)"?

There's no method overload, so 6.26.3 is utterly irrelevant here.

Overload refers to multiple methods with the same name but different parameters being defined on the same class. For example, method f in the example 6.26.1 is overloaded:

class A extends B {}
def f(x: B, y: B) = . . .
def f(x: A, y: B) = . . .
val a: A
val b: B

Implicit parameter resolution precedence is a completely different rule, and one which has a question and answer already on Stack Overflow.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Page 106 of Scala Language Reference says that's how implicit parameters are resolved in case there are multiple candidates. Could you link to the question? If you're talking about http://stackoverflow.com/questions/6966825, that's using 6.26.3, and doesn't mention local vs imported. – Eugene Yokota Dec 24 '11 at 20:36
  • Ok, I did find your http://stackoverflow.com/questions/5598085, which is referring to Josh's nescala talk. The talk is what motivated me to find out where in Scala Language Reference (or even scalac implementation) it's referring those precedences. – Eugene Yokota Dec 24 '11 at 20:57
  • @EugeneYokota I see your point, and it isn't clear indeed, but I suspect the text you quoted refers to "second, eligible are also all implicit members of some object that belongs to the implicit scope of the implicit parameter’s type, T ." Particularly, since this text is _already_ establishing a precedence (though not specific enough to answer your question). – Daniel C. Sobral Dec 24 '11 at 21:49