Quasiquotes are amazing—they make writing macros in Scala hugely less painful, and in my experience they almost always just work exactly as I'd expect. And best of all, they're now available as a plugin in Scala 2.10.
This question is about a small problem that I ran into while writing this blog post. It's on my list of stuff to look into when I can find a few minutes, but I figured I'd post it here in case someone else can beat me to it, and to help other people who are running into the same question.
Suppose I have a list of lists of name-type pairs:
val pss = List(
List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
List(newTermName("z") -> typeOf[String])
)
I want to turn these into a tree that looks like this:
def foo(x: Int, y: Char)(z: String) = ???
The following works just fine:
q"def bar(${pss.head.head._1}: ${pss.head.head._2}) = ???"
That is, it builds the following tree:
def bar(x: Int) = ???
Which suggests that I should be able to write something like this:
val quoted = pss.map(_.map { case (n, t) => q"$n: $t" })
q"def foo..${quoted.map(ps => q"($ps)")} = 1"
Or a little more simply, with multiple parameters in a single parameter list:
q"def baz(..${quoted.head}) = ???"
Neither works—I get errors like this:
<console>:28: error: type mismatch;
found : List[c.universe.Typed]
required: List[c.universe.ValDef]
q"def baz(..${quoted.head}) = ???"
^
Fair enough—I can see how it would look to the quasiquoter like I'm building typed expressions rather than defining parameters in quoted
. None of the obvious things I could think to try worked (adding = _
, explicitly typing the quasiquote as a ValDef
, etc.).
I know that I can build the parameter definitions manually:
val valDefs = pss.map(
_.map {
case (n, t) => ValDef(Modifiers(Flag.PARAM), n, TypeTree(t), EmptyTree)
}
)
And now the baz
version (with one parameter list) works:
q"def baz(..${valDefs.head}) = ???"
But not the foo
version (the one with multiple parameter lists).
So there are two questions here. First, how can I use quasiquotes to turn a name-type pair into a parameter ValDef
outside of the context of a quoted parameter list? And second, how can I turn a list of lists of parameter definitions into multiple parameter lists?
It's easy enough to fall back to manual AST construction for the whole damn thing (see my post for an example), but I'd like to be able to use quasiquotes instead.
val
works perfectly here—thanks!...$quoted
doesn't, though—I'd guess because the inner lists aren't automatically lifted to parameter lists? – Pelican