3
votes

julia 0.5.1

I want to create a function inside a quote that can be used after the specified macro has been used. Here is an example of what I mean

macro wat()
  quote
    type Foo end
    global bar() = begin end
    global function bar2()
    end
    type Baaz end
  end
end

@wat
Foo()
Baaz()
bar()
bar2()

Now when I run this the last line crashes, because bar2 is undefined. I do not understand why because in my understanding bar() and bar2() should be equal and bar is just syntactic sugar for bar2. But they are apparently not equal and I do not understand why the one works and other does not.

Secondly is there a way to define bar and bar2 inside that quote without the global-keyword and still being available after the macro has been executed?

My motivation for wanting the bar2 notation is that I can specify a return-type with this syntax.

global bar3()::Void = begin end

Is not allowed syntax.

1
Because of macro hygiene (docs.julialang.org/en/release-0.5/manual/metaprogramming/…), names of local variables in the returned expression are replaced with unique symbols. Therefore, you need to use esc on symbols or expressions with local variables that should be left alone. However, I am not sure why global does not work for bar2. Use macroexpand(:(@wat)) to see the generated code. - tim
The macroexpand shows me that some kind of symbols have been added to the bar2-function it looks like this global function #3bar2()... the bar() function does not have that kind of stuff around it. But how do I prevent julia from adding some crap around my function? - user2329125
@tim Putting esc around the quote does the trick, if you want you can answer. Otherwise I will answer myself tomorrow with your answer ;) - user2329125
Unrelated to the question, when you say crashes it suggest a system exception taking down Julia with it - very bad. It is better to say the last line gave an error, or threw an exception - Dan Getz

1 Answers

5
votes

In the returned expressions of Julia macros, names of local variables are replaced with unique symbols:

julia> macro foo()
        quote
            x = 1
            global y = 2
        end
    end
@foo (macro with 1 method)

julia> macroexpand(:(@foo))
quote  # REPL[1], line 4:
    #1#x = 1
    global y = 2
end

This feature is called macro hygiene and avoids accidental clashes with variables at the call site.

To escape from this behavior, one has to use esc:

julia> macro bar()
                quote
                    x = 1
                end |> esc
            end
@bar (macro with 1 method)

julia> macroexpand(:(@bar))
quote  # REPL[1], line 3:
    x = 1
end

Often, one doesn't want to escape the whole returned expression but only specific parts of it:

julia> macro myshow(expr)
        quote
            x = $(esc(expr))
            println($(string(expr)), " = ", x)
            x
        end
    end
@myshow (macro with 1 method)

julia> x = pi/2
1.5707963267948966

julia> macroexpand(:(@myshow sin(x)))
quote  # REPL[1], line 3:
    #1#x = sin(x) # REPL[1], line 4:
    (Main.println)("sin(x)", " = ", #1#x) # REPL[1], line 5:
    #1#x
end

julia> @myshow sin(x)
sin(x) = 1.0
1.0

julia> x
1.5707963267948966

For details, I recommend to read the corresponding section in the manual.