The best practice is to only apply esc to specific parts of the returned expression which need it. So in your case, I would write your macro as
macro moo3(args...)
v = [:($(esc(x)) = foo($(string(x)))) for x in args]
return Expr(:block, v...)
end
because then foo does not get escaped.
Here’s a demonstraction of why:
julia> let
foo(x) = x #some user locally redefines `foo(x)`
@moo2 w1 w2
w1, w2
end
("w1", "w2")
uh-oh, this isn’t the output we intended!
Now look at how @moo3 deals with this:
julia> let
foo(x) = x
@moo3 w1 w2
w1, w2
end
("w1-w1", "w2-w2")