On macro hygiene

So I have this macro:

macro partial(call)
  Expr(:function, :((rest...,)), Expr(:call, call.args..., :(rest...)))
end

for partial function application.

timestwo = @partial *(2)
timestwo(5) # 10; works fine

rest = 7 # name `rest` clashes with parameter name in function generated by macro.

timesrest = @partial *(rest)
timesrest(5) # MethodError: no method matching *(::Tuple{Int64}, ::Int64)

if I esc the second expression (Expr(:function, :((rest...,)), esc(Expr(:call, call.args..., :(rest...))))) then it ignores the value of the parameter and uses the captured rest twice.

What is the best solution here? Is copying the args off the call even correct?

Thanks!

EDIT: I have misunderstood the use of esc as it is made to violate hygiene. eval solves the proble of name clashing, but when the variable rest is changed between the creation of timesrest and its invocation, the old value is used.

Escape the arguments:

macro partial(call)
    Expr(:function, :((rest...,)), Expr(:call, esc.(call.args)..., :(rest...)))
end

Now:

julia> rest = 7;

julia> timesrest = @partial *(rest);

julia> timesrest(5)
35

Note that I would probably not use a macro for this. I usually find it easier to read and work with other constructs, such as using anonymous functions directly in this case:

julia> timestwo = x -> 2 * x;

julia> timestwo(5)
10

julia> rest = 7;

julia> timesrest = x -> rest * x;

julia> timesrest(5)
35
3 Likes

Awesome! Exactly what I needed. I know that in this case it doesn’t make much sense, but for a project I’m making it’ll be useful