How to construct `Expr(:tuple, ...)` using templates

When evaluating an expression tree, the only thing that has any meaning is an Expr object (to a first approximation at least). Everything else is treated as a black box by the compiler and simply left alone. For example:

type Foo
  x
end

eval(Expr(:call, :+, :a, Foo(:(b+c))))
# which is equivalent to
a + Foo(:(b+c))
# NOT to
a + Foo(b + c)

If you evaluate this expression, b+c will never be evaluated, because a Foo has to be left as-is. In your case you’re working with a Tuple rather than a Foo but the principle is exactly the same.

So in your example, what’s happening is that the compiler looks for a set of names to serve as the argument list, but it only finds a value it isn’t allowed to look at – since it can only look inside an Expr(:tuple).

In theory there could be rules for how tuples (or arbitrary objects) are interpreted as expressions so that there’s effectively no difference between :(a, b) and (:a, :b). This would make your example behave more intuitively, but would also make the boundary between code and data in Expr objects much more complex.

Tuple expressions can be constructed in the normal way by quoting them: :(a, b), :(foo((1,2))) etc. And you can splice into them as usual as well; the trick is to start with a one-element tuple expression :(a,) and unquote-splat :($(xs...),), making the final version:

macro_helper1(arguments, form) = :(($(arguments...),) -> $form)
1 Like