Using macros to generate NamedTuples

I am a bit confused by using macros to generate expressions. I would like to have a macro to make NamedTuples as follows:

a = 1
b = 2
@make_nt a b

which should return an expression that evaluates to NamedTuple{(:a, :b)}((a, b)) or (a=1, b=2)

however

macro make_nt_try(args...)
    return :(NamedTuple{$args}($args))
end

returns (a = :a, b = :b)

and

macro make_nt_expr(args...)
    return Expr(:(NamedTuple{$args}), args)
end

raises TypeError: in Expr, expected Symbol, got Expr

and

macro make_nt(args...)
    return :(NamedTuple{$args}(tuple($(args...))))
end

works outside of functions, but not for variables in the local scope of functions.
I have read the metaprogramming section of the documentation, but did not understand when exactly evaluation happens and why it does not happen within the local scope.

@macroexpand @make_nt(a, b)

always gives

:(Main.NamedTuple{(:a, :b)}(Main.tuple(Main.a, Main.b)))

even when it is called inside a function.

Is it possible to make a macro that works properly inside function, or is my mental model for how macros should be used wrong in some way?

Looks like like you’re missing an esc. See the macro hygiene section of the documentation.

Does this do what you want?

macro make_nt(args...)
    return :(NamedTuple{$args}(tuple($(esc.(args)...))))
end
2 Likes

It does, thank you for the quick reply! I will look deeper in the escaping and macro hygiene parts to understand it.