Macro hygene escape puzzle

I want to define a macro similar to those in the code below, where the function f is evaluated in the scope where the macro is expanded, but default is evaluated in module A.

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.5.3 (2020-11-09)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> module A
macro p() quote $(esc(:f))(x=default) = 1 end end
macro q() quote function $(esc(:f)) end end end
macro r() quote function $(:f) end end end
macro s() quote f(x=default) = 1 end end
macro t() :( f(x=default) = 1 ) end
end
Main.A

julia> using .A: @p, @q, @r, @s, @t

julia> @macroexpand @p
quote
    #= none:2 =#
    f(var"#1#x" = Main.A.default) = begin
            #= none:2 =#
            1
        end
end

julia> @macroexpand @s
quote
    #= none:5 =#
    var"#2#f"(var"#3#x" = Main.A.default) = begin
            #= none:5 =#
            1
        end
end

This is consistent with the manual. Defining f as a function is like assigning to it, so it’s a local variable. (It would be nice if the manual said that explicitly.) Unless it is escaped, it is replaced by a gensym.

julia> @macroexpand @q
:($(Expr(:error, "malformed expression")))

julia> @macroexpand @r
quote
    #= none:4 =#
    function var"#4#f" end
end

That’s odd: esc(:f) works in some contexts, but not in others. Is there a way to do what @q is trying to do?

julia> @macroexpand @t
:(Main.A.f(var"#5#x" = Main.A.default) = begin
          #= none:6 =#
          1
      end)

That’s also odd. By my reading of the manual, f should refer to the same thing in @t as it does in @s.

1 Like

The difference between @s and @t looks like a bug to me. From the documentation of quote:

  Unlike the other means of quoting, :( ... ), this form introduces QuoteNode elements to the
  expression tree, which must be considered when directly manipulating the tree. For other purposes,
  :( ... ) and quote .. end blocks are treated identically.

This is a known issue and should be fixed on Julia nightly. See fix #38386, macro defining empty function with escaped name by JeffBezanson · Pull Request #38402 · JuliaLang/julia · GitHub

1 Like