5

The following macro is pasted from http://docs.scala-lang.org/overviews/quasiquotes/usecases.html:

import reflect.macros.Context
import language.experimental.macros
val universe = reflect.runtime.universe; import universe._
import reflect.runtime.currentMirror
import tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()

object debug {
  def apply[T](x: =>T): T = macro impl
  def impl(c: Context)(x: c.Tree) = { import c.universe._
    val q"..$stats" = x
    val loggedStats = stats.flatMap { stat =>
      val msg = "executing " + showCode(stat)
      List(q"println($msg)", stat)
    }
    q"..$loggedStats"
  }
}

It produces this error message in Scala 2.11.1:

Q.scala:9: error: macro implementation reference has wrong shape. required:
macro [<static object>].<method name>[[<type args>]] or
macro [<macro bundle>].<method name>[[<type args>]]
  def apply[T](x: =>T): T = macro impl
                                  ^

I've tried varying the code in numerous ways (import reflect.macros.whitebox.Context, import reflect.macros.blackbox.Context, putting scala. at the start of each import, making the arguments consistently by-name or consistently by-value, macro impl[T], getting rid of the type parameter, macro debug.impl, putting the apply after the impl, and more) with no success. What am I doing wrong? Is it something in the imports? Those come (mostly) from a different web page, http://docs.scala-lang.org/overviews/quasiquotes/setup.html.

The same error occurs on both of the other example macros from that page:

object Macro {
  def apply(x: Int): Int = macro impl
  def impl(c: Context)(x: c.Expr[Int]): c.Expr[Int] = { import c.universe._
    c.Expr(q"$x + 1")
  }
}

object Macro {
  def apply(x: Int): Int = macro impl
  def impl(c: Context)(x: c.Tree) = { import c.universe._
    q"$x + 1"
  }
}
Ben Kovitz
  • 4,920
  • 1
  • 22
  • 50
  • The little "Just in time compilation" example on the same page also fails, though with a different error: `toolbox.compile(code)` fails because it wants `code` to be `this.toolbox.u.Tree` but actually it's `this.universe.Tree`. I figure the documentation has probably just gotten out of sync with 2.11.1. I've haven't been able to guess what needs to change, though. – Ben Kovitz May 23 '14 at 15:38
  • Could you post an end-to-end sbt project that doesn't work? When I tried to compile your first example, it did succeed. – Eugene Burmako May 24 '14 at 08:26
  • @EugeneBurmako I'm not using _sbt._ I'm just using the exact text file posted above, and running it from the command line as `scala Q.scala`. Maybe this is the problem. Should I add something to the command line? – Ben Kovitz May 24 '14 at 09:09

1 Answers1

5

scala Foo.scala wraps the code that you feed to it in an anonymous class. As a result, something like:

import scala.reflect.macros.Context
import scala.language.experimental.macros

object debug {
  def apply[T](x: T): T = macro impl
  def impl(c: Context)(x: c.Tree): c.Tree = ???
}

Gets transformed into:

[[syntax trees at end of                    parser]] // Test.scala
package <empty> {
  object Main extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    def main(args: Array[String]): scala.Unit = {
      final class $anon extends scala.AnyRef {
        def <init>() = {
          super.<init>();
          ()
        };
        import scala.reflect.macros.Context;
        import scala.language.experimental.macros;
        object debug extends scala.AnyRef {
          def <init>() = {
            super.<init>();
            ()
          };
          <macro> def apply[T](x: T): T = impl;
          def impl(c: Context)(x: c.Tree): c.Tree = $qmark$qmark$qmark
        }
      };
      new $anon()
    }
  }
}

However, macro implementations must be defined in static object or bundles, which is what the error messages tries to say:

/Users/xeno_by/Projects/211x/sandbox/Test.scala:5: error: macro implementation reference has wrong shape. required:
macro [<static object>].<method name>[[<type args>]] or
macro [<macro bundle>].<method name>[[<type args>]]
  def apply[T](x: T): T = macro impl
                                ^
one error found

Unfortunately, if something is put into inside an anonymous class, it's no longer static, which means that scala Foo.scala-style development is incompatible with macros. Consider using alternatives, e.g. scalac or sbt.

Eugene Burmako
  • 13,028
  • 1
  • 46
  • 59
  • Thanks. Indeed I did not understand that top-level objects in a script are not static. The compiler output makes clear why they're not. – Ben Kovitz May 24 '14 at 14:12
  • Is the same problem happening in this [question about reflection](http://stackoverflow.com/questions/23843338/why-does-scala-reflection-work-inside-an-object-but-not-at-the-top-level-of-a-sc)? – Ben Kovitz May 24 '14 at 14:26
  • I just ran it with a separate test file with _scalac_ and it worked. Hooray! Now I can start experimenting with macros. BTW, much gratitude for this error message: `(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)`. – Ben Kovitz May 24 '14 at 14:27