4
votes

I'd like to pass the value of a variable to a macro in julia? I tried the following but it didn't work:

macro m(a,b)
  @show a,b
  ex = quote
    $a = eval($b)
  end
  @show ex
  esc(ex)
end

b = 1
@m a $b

Output:

(a, b) = (:a, :($(Expr(:$, :b))))
ex = quote  # REPL[20], line 4:
    a = eval($(Expr(:$, :b)))
end
ERROR: unsupported or misplaced expression $
1

1 Answers

4
votes

You were very close! :)

Macros can have type annotations too, they are functions that work at compile time, but their inputs can only be Exprs, Symbols or constant values, ie: Int, macros don't evaluate their inputs before being invoked, like functions do before being called, macros work on syntax.

julia> macro m(a::Symbol, b)  # in ths method a should allways be a symbol
           # use spaces and parens as needed to delimit and 
           # group args as needed, not commas in this case
           @show a b          # or use this: @show(a, b) notice comma here
           ex = quote         # there is no need to use eval here a macro
               $a = $b        # will compile and evaluate the returning
           end                # expression when invoked
           @show ex
           return esc(ex)     # esc will use surrounding scope instead of gensyms
       end
@m (macro with 1 method)

Do not use eval inside the body of a macro, in this case it is in the body of the returned expression (there are cases when this could be needed, but this is not one of them).

julia> x = 2
2

Honestly I don't know yet how to make interpolations similar to macros like @eval or BenchmarkTools.@btime work (should be explained in the manual, I will have to study the code of those macros), but you don't need $ when invoking the macro here for your simple example.

julia> @m y (x + 1)  # parens for grouping, or @m(y, x + 1)
a = :y               # :y is a symbol
b = :(x + 1)
ex = quote
    #= REPL[17]:4 =#
    y = x + 1
end
3

If you don't use esc it will hygienically create gensyms, in this case it uses the surrounding scope variables instead.

julia> @m z rand(x, y)
a = :z
b = :(rand(x, y))
ex = quote
    #= REPL[17]:4 =#
    z = rand(x, y)
end
2×3 Array{Float64,2}:
 0.817233  0.623775  0.277464
 0.421573  0.443654  0.296359

gensyms look like this:

julia> gensym(:foo)
Symbol("##foo#924")