Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit abstract class constructor parameter and inheritance in Scala

I am fairly new to Scala and have been trying to learn and understand implicit conversions and parameters and have encountered a scenario that I find confusing.

For context, I am using Scaldi to do dependency injection in an Akka application and would like to have multiple injectable actors inherit from an abstract class. I believe I am unable to make the abstract class a trait precisely because we need to make an implicit Injector available via a constructor argument to take advantage of the framework.

A very contrived example that exhibits the behavior that I am seeing is as follows:

class SecretSauce {}

abstract class Base(implicit secretSauce: SecretSauce) {}

class Concrete extends Base {}

object Example extends App {
    ... // Setup Actor system, etc, etc
    implicit val secretSauce: SecretSauce = new SecretSauce()
}

I was expecting things to work but instead I get a compilation error:

Unspecified value parameter secretSauce.
class Concrete extends Base {
             ^

If I add the implicit parameter to the concrete class, like such, things work:

class Concrete(implicit secretSauce: SecretSauce) extends Base {}

I think my confusion stems from how implicit parameters work - in situations like the one I'm describing, are they not inherited by child classes? Can someone ELI5 what is occurring in my example or point me to a reference that can help clear things up?

Thanks!

like image 361
simonl Avatar asked Dec 07 '15 21:12

simonl


2 Answers

The exact rules that determine where the Scala compiler looks for implicits are kind of complicated, but in most situations you only need to think about two places implicit values can come from:

  1. The current scope.
  2. The companion objects for any types involved.

This means this will compile:

class SecretSauce {}

object SecretSauce {
  implicit val secretSauce: SecretSauce = new SecretSauce()
}

abstract class Base(implicit secretSauce: SecretSauce) {}

object Example extends App {
  class Concrete extends Base {}
}

Or this:

class SecretSauce {}

abstract class Base(implicit secretSauce: SecretSauce) {}

object Example extends App {
  implicit val secretSauce: SecretSauce = new SecretSauce()

  class Concrete extends Base {}
}

In your version, though, when the compiler gets to this line:

class Concrete extends Base {}

It will know that it needs to find an implicit SecretSauce value, and it will look first at the implicit values in scope at that line and next in the SecretSauce companion object (if it exists). It doesn't find either, so it refuses to compile your code.

like image 168
Travis Brown Avatar answered Sep 17 '22 19:09

Travis Brown


The implicit parameter get "resolved" from:

  • Implicits defined in current scope
  • Explicit imports
  • Wildcard imports

In order to define class Concrete as far as I understand, the implicit needs to be defined or imported.

I find a very good explanation in this SO answer.

like image 31
Filippo Vitale Avatar answered Sep 18 '22 19:09

Filippo Vitale