Reputation: 21071
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?
Upvotes: 5
Views: 6524
Reputation: 2738
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
Upvotes: 9