Scala – Implicit parameters resolution from super trait

implicitscala

I am trying use implicit parameters to "inject" a dependency into my classes like this:

trait Bar {
    def m:String
}

object Bar {
    implicit val objBar = new Bar{ val m = "from object" } 
}

trait FooTrait { 
    def aBar(implicit bar:Bar) = bar
    def go = { aBar.m }
}

Here the compiler supplies the implicit argument to FooTrait from the implicit val in Bar's companion object. So doing:

scala> println((new FooTrait{}).go)
from object

Gives me the result I expect. However, if I mix the FooTrait and another trait like:

trait SuperFoo {
    implicit val superBar = new Bar{ val m = "from super" }
}

The result is the same:

scala> println((new FooTrait with SuperFoo).go)
from object

I thought that the compiler would look in the SuperFoo before trying to resolve the implicit argument by checking Bar's companion object. This blog post states:

There are very strict rules for which implicit value is to be applied
to a implicit parameter. A simple way to think about it is that the
"closest" definition will be used. Local scope, enclosing class,
parent class, companion object of the desired type.

Am I missing something or is this a known limitation of scalas implicit parameters?

Best Answer

Call to aBar is defined inside FooTrait. When this trait compiles, there is no proper implicits in local scope, enclosing class, or parent class. Compiler doesn't try to re-find implicits when you mix in another trait later. So it just uses default implicit from companion object.

You can get value from SuperFoo if you override method go:

scala> println((new FooTrait with SuperFoo {override def go = {aBar.m}}).go)
from super

You can also redefine you class hierarchy to get your implicit from parent trait:

trait BarHolder { 
    implicit val superBar: Bar
}
trait FooTrait extends BarHolder { 
    def aBar(implicit bar:Bar) = bar
    def go = { aBar.m }
}
trait DefaultFoo extends BarHolder {
    val superBar = implicitly[Bar]
}
trait SuperFoo extends BarHolder {
    val superBar = new Bar{ val m = "from super" }
}

and use it this way:

scala> println((new FooTrait with DefaultFoo).go)
from object

scala> println((new FooTrait with SuperFoo).go)
from super
Related Topic