pfn
pfn

Reputation: 1830

Deferring implicit resolution in macros

So, lets say I have a few things before hand:

case class M[A](val value: A) {
  def apply(as: M[A]*) = macro applyImpl[A]
}

def tpAware(implicit ev: Context[A]): M[A]

val ma: M[X] = ???

I want to do:

ma(tpAware)

Where applyImpl is defined something like

def applyVG[A : c.WeakTypeTag, V](c: MacroContext)(vs: c.Expr[M[V]]*): c.Expr[M[A]] = {
  import c.universe._
  val container = c.prefix.tree.collect {
    case a@Apply(n, xs) => xs
  }.flatten.apply(1)

  val expr = reify {
    M {
      implicit val ev = Context[A]()
      val container = c.Expr[A](container).splice
      sequence(c)(vs).splice foreach { c =>
        container.add(c.value)
      }
      container
    }
  }
  expr
}

The problem now, is that the typer runs before the macro is expanded, and my Context[A] is not visible prior to the application of apply

Wat do?

Upvotes: 0

Views: 149

Answers (1)

pfn
pfn

Reputation: 1830

Rather than injecting an implicit parameter into the argument list, the only approach that works is to create @compileTimeOnly functions which will have an alternative AST injected in their place by the apply macro.

The argument list of the @compileTimeOnly function will be spliced into into the generated AST and that generated AST will be spliced into the overall program at large.

Particular care must be taken to fix symbol ownership and type checking of the generated and spliced syntax trees. See macrology201 part 1 steps 19 through 30 for more information on how to fix the trees properly.

This approach is used by scala-async, sbt, and others to have cooperation occur between macros and their children.

Upvotes: 1

Related Questions