Emil L
Emil L

Reputation: 21071

Implicit parameters resolution from super trait

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

Answers (1)

incrop
incrop

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

Related Questions