scalamacrosscala-macrosimplicits

Conditionally generating implicits in scala


I am working on a system of chained implicit functions, which is similar to the simplified example below. The test c1.payload == c2.payload represents a test I need to do that is not in "type-space"; I had expected that I would drop into a macro for the definition of witnessEvidence, however Scala apparently does not allow macro definitions with implicit arguments of arbitrary type (WeakTypeTag values only!), and so I am a bit stumped about how to proceed with this. The code below shows logically what I'd like to happen, however an implicit function can't conditionally produce or not produce evidence (unless it is inside a macro implementation).

case class Capsule[T](payload: Int)

trait A
trait B
trait C

implicit val capa = Capsule[A](3)
implicit val capb = Capsule[B](3)
implicit val capc = Capsule[C](7)

case class Evidence[T1, T2](e: Int)

implicit def witnessEvidence[T1, T2](implicit c1: Capsule[T1], c2: Capsule[T2]): Evidence[T1, T2] = {
  if (c1.payload == c2.payload)
    Evidence[T1, T2](c1.payload)
  else
    // Do not produce the evidence
}

def foo[T1, T2](implicit ev: Evidence[T1, T2]) = ev.e

val f1 = foo[A, B] // this should compile
val f2 = foo[A, C] // this should fail with missing implicit!

Solution

  • This would not be possible as-is, since the implicit resolution is done at compilation, while testing for value equivalence is done at runtime.

    To make this work, you need to make the compiler understand values as types, so that you can ask for the type equality of the two 3s and use that to infer that capa =:= capb. To do that you can use singleton types: https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#singleton-typed-literals

    If you need to do arithmetic beyond plain equality comparison, you will need to use Nat:https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/nat.scala