Skip to content

Surprising Macro Hygiene behavior: recursive macro invocations are unexpectedly sharing variable names #32603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
NHDaly opened this issue Jul 16, 2019 · 2 comments
Labels
macros @macros

Comments

@NHDaly
Copy link
Member

NHDaly commented Jul 16, 2019

This behavior was surprising to me: in this recursive macro expansion, the nested anonymous functions all share the same gensym'd name for their paramater x:

julia> macro h_m(n,z)
           if n==0 z
           else :((x -> @h_m($(n-1), x + $z))($n))
           end
       end
@h_m (macro with 1 method)

julia> @macroexpand @h_m(3,1)
:(((#2#x->begin
          #= REPL[1]:3 =#
          ((#2#x->begin
                  #= REPL[1]:3 =#
                  ((#2#x->begin
                          #= REPL[1]:3 =#
                          #2#x + (#2#x + (#2#x + 1))
                      end))(1)
              end))(2)
      end))(3))

And so what I intended to return 7 (3 + 2 + 1 + 1) instead returns 4 (1 + 1 + 1 + 1):

julia> @h_m(3,1)
4

I can manually produce the code I expected with a let-block, but i expected it to do this automatically:

julia> macro h_m(n,z)
           if n==0 z
           else let x = gensym(:x)
                   :(($x -> @h_m($(n-1), $x + $z))($n))
               end
           end
       end
@h_m (macro with 1 method)

julia> @h_m(3,1)
7
@ararslan ararslan added the macros @macros label Jul 16, 2019
@maleadt
Copy link
Member

maleadt commented Jul 17, 2019

#23221 ?

@NHDaly
Copy link
Member Author

NHDaly commented Jul 17, 2019

#23221 ?

No, I don't think so. As far as I can tell, I don't think they're related. I don't think this behavior is related to esc() (I didn't use it all above), which seems to be the bulk of the complaint in #23221.

Also, I just checked, and julia 0.6 doesn't produce what I expected either. It does produce a different (though still undesirable, and arguably worse) result, so I guess the changes in the macro expander are maybe relevant to this issue as well:

julia> VERSION
v"0.6.4"

julia> macro h_m(n,z)
                  if n==0 z
                  else :((x -> @h_m($(n-1), x + $z))($n))
                  end
              end
@h_m (macro with 1 method)

julia> @macroexpand @h_m(3,1)
:(((#20#x->begin  # REPL[16], line 3:
            ((#21#x->begin  # REPL[16], line 3:
                    ((#22#x->begin  # REPL[16], line 3:
                            Main.#22#x + (Main.#21#x + (Main.#20#x + 1))
                        end))(1)
                end))(2)
        end))(3))

julia> @h_m(3,1)
ERROR: UndefVarError: #25#x not defined
Stacktrace:
 [1] (::##7#10)(::Int64) at ./REPL[16]:3

In 0.6, it kept the gensym'd names associated with the correct parameters like I expected, but it also marked them as globals, which definitely seems wrong: it should be one or the other, not both. So I think the current behavior is definitely better than it was in 0.6, though you're right that there was also a regression in the name resolution aspect I cared about.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
macros @macros
Projects
None yet
Development

No branches or pull requests

3 participants