gbasler
gbasler

Reputation: 319

Avoiding recompilation with quasiquotes

Quasiquotes simplify many things when writing macros in Scala. However I noticed that macros containing quasiquotes could be recompiled every time a compilation in SBT is triggered, even though neither the macro implementation nor any of it's call sites have changed and need recompilation.

This doesn't seem to happen, if the code in quasiquotes is fairly simple, it seems to happen only if there's a dependency on another class. I noticed that rewriting everything with "reify" seems to solve the recompilation problem but I don't manage to rewrite the last part without quasiquotes...

My macro avoids reflection on startup by creating wrapper functions during compilation.

I have the following classes:

object ExportedFunction {
  def apply[R: Manifest](f: Function0[R], fd: FunctionDescription): ExportedExcelFunction = new ExcelFunction0[R] {
    def apply: R = f()
    val functionDescription = fd
  }

  def apply[T1: Manifest, R: Manifest](f: Function1[T1, R], fd: FunctionDescription): ExportedExcelFunction = new ExcelFunction1[T1, R] {
    def apply(t1: T1): R = f(t1)
    val functionDescription = fd
  }

  ... and so on... until Function17...
}

I then analyze an object and export any member function using the described interface like so:

def export(registrar: FunctionRegistrar,
           root: Object,
           <...more args...>) = macro exportImpl

def exportImpl(c: Context)(registrar: c.Expr[FunctionRegistrar],
                           root: c.Expr[Object],
                           <...>): c.Expr[Any] = {             

  import c.universe._   

  <... the following is simplified ...>
  root.typeSignature.members.flatMap {
    case x if x.isMethod =>
      val method = x.asMethod     

      val callee = c.Expr(method))

      val desc = q"""FunctionDescription(<...result from reflective lookup...>)"""
      val export = q"ExportedFunction($callee _, $desc)"
      q"$registrar.+=({$export})"

I can rewrite the first and last line with reify but I don't manage to rewrite the second line, my best shot is with quasiquotes:

val export = reify {
  ...
     ExportedFunction(c.Expr(q"""$callee _"""), desc)
  ...
}.tree

But this results in:

overloaded method value apply with alternatives... cannot be applied to (c.Expr[Nothing], c.universe.Expr[FunctionDescription])

I think the compiler is missing the implicits, or maybe this code will only work for a function with a fixed number of arguments, since it needs to know at macro compile time how many arguments the method has? However it works if everything is written using quasiquotes...

Upvotes: 3

Views: 258

Answers (2)

gbasler
gbasler

Reputation: 319

For the record, these SBT settings (upgrade to newer version) fixed it:

...
settings = Seq(
  libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value,
  libraryDependencies += "org.scalamacros" % "quasiquotes" % "2.0.0-M3" cross CrossVersion.full,
  autoCompilerPlugins := true,
  addCompilerPlugin("org.scalamacros" % "paradise" % "2.0.0-M3" cross CrossVersion.full)
)

Upvotes: 0

Eugene Burmako
Eugene Burmako

Reputation: 13048

From the description of the SBT problem I can assume that you're using macro paradise for 2.10.x and are facing https://github.com/scalamacros/paradise/issues/11. I was planning to address this issue this week, so the fix should arrive really soon. In the meanwhile you could use a workaround described on the issue page.

As for the reify problem, not all quasiquotes can be rewritten using reify. Limitations such as the one you have faced here were a very strong motivator towards shifting our focus to quasiquotes in Scala 2.11.

Upvotes: 6

Related Questions