Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit parameters resolution from super trait

Tags:

scala

implicit

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?

like image 984
Emil H Avatar asked May 03 '12 11:05

Emil H


People also ask

How do you pass an implicit parameter?

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.

What are implicit parameters?

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.

Can trait take parameters?

Scala 3 allows traits to have parameters, just like classes have parameters. Arguments to a trait are evaluated immediately before the trait is initialized.

Can traits have parameters Scala?

Unlike a class, Scala traits cannot be instantiated and have no arguments or parameters. However, you can inherit (extend) them using classes and objects.


1 Answers

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
like image 105
incrop Avatar answered Nov 15 '22 05:11

incrop