Search code examples
scalaimplicits

Setting implicit instance variable for Scala's object


I have a Scala object with a bunch of utility-methods, each method makes use of an implicit method parameter s

object MyObject {
 def a(implicit s:String) = ???
 def b(implicit s:String) = ???
 def c(implicit s:String) = ???
}

I don't like that fact that I need this implicit s:String in each parameter list. Is there a way to set the variable s only once (i.e. when object is first accessed). For a class I would do something like this:

class MyClass(implicit s:String) {
 def a() = ???
 def b() = ???
 def c() = ???
}

Solution

  • Short answer is "No you can't".

    Why? Because s is not a variable, it's a parameter. So it is not set at some point at runtime (say, on first accession), but resolved (because it is implicit) at compile time.

    When you write MyObject.a in a part of your code, the compiler will check if, in the current scope, there is an implicit String definition, and will use that definition for s.

    If you then use a in another part of your code, it will use the implicit definition in that scope, which may very well be different.

    All this is verified at compile-time, so it is not related to when the methods are accessed at runtime (although when they are accessed may change the runtime value for s, if you have mutable implicits, which should be avoided).

    If you always want to use the same definition for s, why not simply put this definition in your object? Or import it as an implicit:

    object MyObject {
      implicit def myImplicitString: String = ??? //first possibility
      import MyImplicits.stringImplicit //second possibility, if stringImplicit is indeed defined as implicit
    }
    

    The bottom line is, if you don't want to pass the implicit as parameter, you need to have it in scope, and since your scope is an object, it must always be the same.

    However, what's wrong with using a class? It gives you what you want (avoiding to repeat the implicit parameter) without changing much your calls (just use new MyClass().a instead of MyObject.a, or even MyCaseClass().a if you define it as a case class). If you have other fields in your object, you can always put them in a companion object to your class, to minimize the overhead at instantiation.