Skip to content
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

Symbols passed to macros in a module improperly scoped #15085

Closed
jballanc opened this issue Feb 15, 2016 · 12 comments
Closed

Symbols passed to macros in a module improperly scoped #15085

jballanc opened this issue Feb 15, 2016 · 12 comments

Comments

@jballanc
Copy link
Contributor

When a macro is imported from a foreign module, if a symbol is passed as an argument to that macro and used in an escape clause within a quote clause, the symbol has the macro-source-module name prefixed. This does not seem like desirable behavior.

Example:

julia> module X 
           macro p(y) 
             quote 
               println($y) 
             end 
           end 
       end 
X 

julia> using X 

julia> test = "Hello, world" 
"Hello, world" 

julia> @X.p(test) 
ERROR: UndefVarError: test not defined 

julia> macroexpand(:(@X.p(test)))
quote  # none, line 4:
    X.println(X.test)
end

Currently, this can be avoided by a bit of quote-escape-gymnastics:

julia> module X 
           macro q(y) 
             quote 
               println(:($($y))) 
             end 
           end 
       end

julia> @X.q(test) 
Hello, world

julia> macroexpand(:(@X.q(test)))
quote  # none, line 9:
    X.println(test)
end

This is currently causing bugs and other issues for at least a few users as discussed here and here.

@jballanc
Copy link
Contributor Author

It's been suggested that there is another workaround:

julia> module X 
  macro p(y) 
    esc(quote 
      println($y) 
    end) 
  end 
end

Though I'd still argue that this is unintuitive and contrary to most implementations of symbol hygiene.

@JeffBezanson
Copy link
Member

That isn't really a workaround; it's how it's supposed to be written at the moment. However see #10940.

@jballanc
Copy link
Contributor Author

So, if I understand, there's not any good way to mix module defined values with arguments passed to a macro if the arguments might be symbols? i.e.:

julia> module Foo
         greeting = "Hello"
         macro bar(thing)
           esc(quote
             println("$(greeting), $(thing)")
           end)
         end
       end
Foo

julia> using Foo

julia> test = "World"
"World"

julia> @Foo.bar(test)
ERROR: UndefVarError: greeting not defined

julia> macroexpand(:(@Foo.bar(test)))
quote  # none, line 5:
    println("$(greeting), $(thing)")
end

@tomaklutfu
Copy link
Contributor

You can change scope of esc. But it cannot go in quote ... end. Below code works but there can be more concise one.

module Foo
         greeting = "Hello"
         macro bar(thing)
                  Expr(:call,:println,
                          Expr(:string,:greeting,",",esc(thing)))
         end
end

@JeffBezanson
Copy link
Member

You can use esc anywhere, e.g.

julia> module Foo
         greeting = "Hello"
         macro bar(thing)
           quote
             println("$(greeting), $(esc(thing))")
           end
         end
       end

@tomaklutfu
Copy link
Contributor

@JeffBezanson it did not work for me macroexpand shows how it decorated with Foo .

quote  # In[1], line 5:
    Foo.println("$(Foo.greeting), $(Foo.esc(Foo.thing))")
end

At first I thougt string interpolation is the problem but I slightly changed it to see quote ... end does not work with esc

julia> module Foo
               greeting = "Hello"
               macro bar(thing)
                    quote
                         println("$(greeting), $(esc(thing))")
                         esc(thing)
                   end
              end
         end

With macroexpand, I got this

quote  # In[13], line 5:
    Foo.println("$(Foo.greeting), $(Foo.esc(Foo.thing))") # In[13], line 6:
    Foo.esc(Foo.thing)
end

This is on

julia> versioninfo()
Julia Version 0.5.0-dev+2600
Commit 746fa80* (2016-02-12 07:38 UTC)
Platform Info:
  System: Linux (arm-linux-gnueabihf)
  CPU: ARMv7 Processor rev 0 (v7l)
  WORD_SIZE: 32
  BLAS: libopenblas (NO_AFFINITY ARMV7)
  LAPACK: libopenblas
  LIBM: libm
  LLVM: libLLVM-3.7.1

@JeffBezanson
Copy link
Member

String interpolation is actually the problem here. You need two $s; one for string interpolation and one to interpolate syntax into the string interpolation:

julia> module Foo
              greeting = "Hello"
              macro bar(thing)
                  quote
                         println("$($greeting), $($(esc(thing)))")
                  end
             end
        end
Foo

julia> macroexpand(:(@Foo.bar x))
quote  # none, line 5:
    Foo.println("Hello, $(x)")
end

In this case, calling string with function call syntax or passing the arguments directly to println is easier than using string interpolation.

If you just put esc(thing) inside quote, the whole thing is quoted so esc doesn't do anything. You need to call esc while creating the quoted block.

@JeffBezanson
Copy link
Member

Typical usage looks like this:

quote
    println($(esc(thing)))
end

@tomaklutfu
Copy link
Contributor

Ok. I got what you mean and indeed it needs syntax interpolation in quote as $(esc(thing)) and one more $ for string interpolation.

@jballanc
Copy link
Contributor Author

So, out of curiosity, what's the reason every argument to a macro is not esc(...) by default?

Alternatively, I think one might reasonably ask why namespace qualification is part of the hygiene pass? For example, in Clojure the equivalent qualification is handled by the quasi-quote:

boot.user=> (ns foo)
nil
foo=> 'bar
bar
foo=> `bar
foo/bar

@mauro3
Copy link
Contributor

mauro3 commented Feb 16, 2016

@jballanc: I think this is addressed in #6910

@jballanc
Copy link
Contributor Author

@mauro3 Ah, ok...I just went back and re-read that original thread instead. It seems like that issue (and the follow-up in #10940) covers everything, and whatever is not covered is probably better addressed in that thread anyway.

Thanks all for the comments/help understanding Julia's macro/hygiene system!

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

No branches or pull requests

4 participants