Julia: Is it possible to transform `fn(df, @macro1 :a, :b, :c)` into `fn(df, (:a, :b, :c)...)`

Viewed 38

For example, I would like to write a macro @macro1 that transforms

transform(df, @macro :X :Y) to

transform(df, (:X, :Y)...)

Heres' my attempt

macro macro1(ex...)
    println(ex)
    :($ex...)
end

transform(df, @macro1 :X :Y)

This seems quite hard. However, if allow the macro to be on the outside like this

@macro transform(df, (:X :Y))

then it's easier. But I am not sure if the "inside" macro style can achieve this.

1 Answers

I guess you trying to get it working with the comma?

julia> macro splicing(expr)
           return Expr(:(...), esc(expr))
       end
@splicing (macro with 1 method)

julia> @macroexpand tuple(:w, @splicing :x, :y)
:(tuple(:w, (:x, :y)...))

julia> tuple(:w, @splicing :x, :y)
(:w, :x, :y)

The subtle part is that the syntax @m a, b is parsed as @m((a, b)). We can just directly wrap this into the splice call.

Don't try this with actual varargs, though, nasty stack overflows can occur.

You might need a special case for input of size one, since this does not construct a tuple:

julia> tuple(:w, @splicing :x)
ERROR: MethodError: no method matching iterate(::Symbol)
Closest candidates are:
  iterate(::Core.SimpleVector) at essentials.jl:600
  iterate(::Core.SimpleVector, ::Any) at essentials.jl:600
  iterate(::ExponentialBackOff) at error.jl:218
  ...
Stacktrace:
 [1] top-level scope at REPL[34]:1

The following could work, but may turn out fragile:

macro splicing3(expr)
    if Meta.isexpr(expr, :tuple)
        return Expr(:(...), esc(expr))
    else
        return esc(expr)
    end
end
Related