Defining an Anonymous Function in a Macro

I’m working on a macro that defines several functions that all have the same parameters. However, upon evaluation, the keyword arguments of the functions are getting prefixed with Main. as though they are variables that need to be resolved.

MWE:

"""Produce an anonymous function from a block of instructions and argument names."""
function make_anon_function_expr(function_block; args = (), kwargs = nothing)
    sym_args = Any[Symbol(p) for p in args]
    if !(kwargs isa Nothing)
        pushfirst!(sym_args, Expr(:parameters, (Symbol(k) for k in kwargs)...))
    end

    return Expr(
        Symbol("->"),
        Expr(
            :tuple, 
            sym_args...
        ),
        function_block
    )
end

# Test out make_anon_function_expr
func_block = quote
    return x + y
end

f = make_anon_function_expr(func_block; kwargs = (:x, :y))
eval(f)(;x=3, y=2)
# 5

Now with a macro

macro MakeMyStruct(func_block)
    func = make_anon_function_expr(func_block; kwargs = (:x, :y))
    return func
end

b = @MakeMyStruct begin
    return x + y
end
# ERROR: syntax: invalid keyword argument syntax "Main.x"

I’d appreciate your thoughts!

This is a macro hygiene issue. If you use macroexpand, you’ll see that it is trying to use the global variables x and y instead of your new ones:

julia> @macroexpand @MakeMyStruct begin
           return x + y
       end
:((; Main.x, Main.y)->begin
          #= REPL[103]:2 =#
          return Main.x + Main.y
      end)

The fix is to use esc:

julia> macro MakeMyStruct(func_block)
           func = make_anon_function_expr(func_block; kwargs = (:x, :y))
           return esc(func)
       end
@MakeMyStruct (macro with 1 method)

julia> @macroexpand @MakeMyStruct begin
           return x + y
       end
:((; x, y)->begin
          #= REPL[105]:2 =#
          return x + y
      end)

julia> b = @MakeMyStruct begin
           return x + y
       end
#67 (generic function with 1 method)

julia> b(; x = 3, y = 2)
5
3 Likes

Ah, of course! Seeing it now, seems obvious. Thank you!

1 Like