简体   繁体   中英

Importing generic implicits from class instances

I'm trying to make a generic implicit provider which can create an implicit value for a given type, something in the lines of:

trait Evidence[T]

class ImplicitProvider[T] {
  class Implementation extends Evidence[T]
  implicit val evidence: Evidence[T] = new Implementation
}

To use this implicit, I create a val provider = new ImplicitProvider[T] instance where necessary and import from it import provider._ . This works fine as long as there is just one instance. However sometimes implicits for several types are needed in one place

case class A()
case class B()

class Test extends App {
  val aProvider = new ImplicitProvider[A]
  val bProvider = new ImplicitProvider[B]

  import aProvider._
  import bProvider._

  val a = implicitly[Evidence[A]]
  val b = implicitly[Evidence[B]]
}

And this fails to compile with could not find implicit value for parameter and not enough arguments for method implicitly errors.

If I use implicit vals from providers directly, everything starts to work again.

implicit val aEvidence = aProvider.evidence
implicit val bEvidence = bProvider.evidence

However I'm trying to avoid importing individual values, as there are actually several implicits inside each provider and the goal is to abstract them if possible.

Can this be achieved somehow or do I want too much from the compiler?

The issue is that when you import from both objects, you're bringing in two entities that have colliding names: evidence in aProvider and evidence in bProvider . The compiler cannot disambiguate those, both because of how its implemented, and because it'd be a bad idea for implicits, which can already be arcane, to be able to do things that cannot be done explicitly (disambiguating between clashing names).

What I don't understand is what the point of ImplicitProvider is. You can pull the Implementation class out to the top level and have an object somewhere that holds the implicit val s.

class Implementation[T] extends Evidence[T]

object Evidence {
  implicit val aEvidence: Evidence[A] = new Implementation[A]
  implicit val bEvidence: Evidence[B] = new Implementation[B]
}

// Usage:
import Evidence._
implicitly[Evidence[A]]
implicitly[Evidence[B]]

Now, there is no name clash.

If you need to have an actual ImplicitProvider , you can instead do this:

class ImplicitProvider[T] { ... }

object ImplicitProviders {
  implicit val aProvider = new ImplicitProvider[A]
  implicit val bProvider = new ImplicitProvider[B]

  implicit def ImplicitProvider2Evidence[T: ImplicitProvider]: Evidence[T]
    = implicitly[ImplicitProvider[T]].evidence
}

// Usage
import ImplicitProviders._
// ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM