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?
The implicit parameter in Java is the object that the method belongs to. It's passed by specifying the reference or variable of the object before the name of the method. An implicit parameter is opposite to an explicit parameter, which is passed when specifying the parameter in the parenthesis of a method call.
Implicit parameters are the parameters that are passed to a function with implicit keyword in Scala, which means the values will be taken from the context in which they are called.
Scala 3 allows traits to have parameters, just like classes have parameters. Arguments to a trait are evaluated immediately before the trait is initialized.
Unlike a class, Scala traits cannot be instantiated and have no arguments or parameters. However, you can inherit (extend) them using classes and objects.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With