5

Can scala 2.11 macros force their parameters' macros to expand?

Here's my use case: I started with the printf macro from the documentation and then made my own macro to concatenate strings.

def mconcat(s1: String, s2: String, s3: String): String = macro mconcat_impl
def mconcat_impl(c: Context)(s1: c.Expr[String], s2: c.Expr[String], s3: c.Expr[String]): c.Expr[String] = {
  import c.universe._
  c.Expr[String](q"""$s1.concat($s2.concat($s3))""")
}

I hoped to combine these two macros,

mprintf(mconcat("what", "a", "burger"))

but got a match error in macro expansion.

EDIT

Thanks to Travis Brown for pointing out that mconcat doesn't expand to a string literal. Sorry about that! But there's still a problem if we simplify the value of mconcat to:

c.Expr[String](q"""$s1""")

or to

s1

or even to

c.Expr[String](Literal(Constant("what")))

All three give the same error message:

Test.scala:8: error: exception during macro expansion:
  scala.MatchError: ("what": String) (of class scala.reflect.internal.Trees$Typed)
at Printf$.printf_impl(Printf.scala:23)

  mprintf(mconcat("what", "a", "burger"))
         ^
one error found
Ben Greenman
  • 1,945
  • 12
  • 22
  • The full `mprintf` is in the documentation link (scroll halfway down, or search for "a complete definition").The important difference is that `mprintf` matches its argument as a `Literal(Constant(...))`, unlike `mconcat` which just wants well-typed arguments. – Ben Greenman Nov 01 '15 at 21:42
  • 2
    The macro is being expanded—the problem is that `mprintf` expects a string literal, and after expansion its argument in your example is still `"what".concat("a".concat("burger"))`. – Travis Brown Nov 01 '15 at 22:06
  • Ah! Good catch! But even without calling `concat`, I can't seem to thread a string through the `mconcat` macro -- see above. – Ben Greenman Nov 02 '15 at 03:09
  • Both of your simplified versions work for me—I'm not sure where the type ascription in your error message is coming from. – Travis Brown Nov 02 '15 at 14:42
  • 1
    It was the `Context`. I was using `scala.reflect.macros.blackbox.Context`, but the examples work if the parameter to `mconcat` is a `scala.reflect.macros.whitebox.Context`. Do macros always get fully-expanded syntax trees (no unexpanded macros at any depth)? – Ben Greenman Nov 02 '15 at 17:38
  • (Even if macro arguments are fully expanded, a function that forces expansion/type-checking of an AST would be useful for macros that want to call other macros and inspect the results.) – Ben Greenman Nov 04 '15 at 06:40
  • I'm guessing that `blackbox` macros are forced to conform to the desired return type, and nothing else. So, there's no telling whether they produce a complex expression or just a literal, as they are just cast to that type. `whitebox` let you see and manipulate more. The documentation doesn't always make clear distinctions for what you can and can't do with either. – Michael Zajac Nov 18 '15 at 02:55

0 Answers0