How to declare traits as taking implicit "constructor parameters"?

Andrzej Doyle picture Andrzej Doyle · Aug 8, 2011 · Viewed 25.2k times · Source

I'm designing a class hierarchy, which consists of a base class along with several traits. The base class provides default implementations of several methods, and the traits selectively override certain methods via abstract override, so as to acts as stackable traits/mixins.

From a design perspective this works well, and maps to the domain so that I can add a filtering function from here (one trait) with a predicate from here (another trait) etc.

However, now I'd like some of my traits to take implicit parameters. I'm happy that this still makes sense from a design perspective, and wouldn't prove confusing in practice. However, I cannot convince the compiler to run with it.

The core of the problem seems to be that I cannot provide constructor arguments for a trait, such that they could be marked implicit. Referencing the implicit parameter within a method implementation fails to compile with the expected "could not find implicit value" message; I tried to "propagate" the implicit from construction stage (where, in practice, it's always in scope) to being available within the method via

implicit val e = implicitly[ClassName]

but (as no doubt many of you expect) that definition failed with the same message.

It seems that the problem here is that I can't convince the compiler to tag the signature of the trait itself with an implicit ClassName flag, and force callers (i.e. those who mix the trait into an object) to provide the implicit. Currently my callers are doing so, but the compiler isn't checking at this level.


Is there any way to mark a trait as requiring certain implicits be available at construction time?

(And if not, is this simply not implemented yet or is there a deeper reason why this is impractical?)

Answer

Alexey Romanov picture Alexey Romanov · Aug 8, 2011

Actually, I've wanted this quite often before, but just came up with this idea. You can translate

trait T(implicit impl: ClassName) {
  def foo = ... // using impl here
}

to [EDITED: original version didn't provide access to implicit for other methods]

trait T {
  // no need to ever use it outside T
  protected case class ClassNameW(implicit val wrapped: ClassName)

  // normally defined by caller as val implWrap = ClassNameW 
  protected val implWrap: ClassNameW 

  // will have to repeat this when you extend T and need access to the implicit
  import implWrap.wrapped

  def foo = ... // using wrapped here
}