The official documentation says that @eval expr
returns eval(QuoteNode(expr))
. Why not simply expr
?
Does it have something to do with interpolation?
That’s wrong. It’s returning an expression that eval
s the quoted version of expr
, i.e. the expression it returns unquoted is more or less eval($(QuoteNode(expr)))
.
Sorry, but I still can’t see the difference between the following two macros:
macro m1(expr)
eval(QuoteNode(expr))
end
and
macro m2(expr)
expr
end
Isn’t the eval
above being executed at parse time? And Isn’t eval(QuoteNode(x))
just x
, in general?
Could you please provide a minimal example where m1
and m2
show different behavior?
What I mean is that the document is wrong. It also does not mean what you write though. The expression in the doc was meant to be the returned expression of the macro, not the implementation of the macro so not, eval
is not executed at parse time. What I said is that the implementation is more or less return :(eval($(QuoteNode(expr))))
Thanks, that helped. Is there a way to see the rewriting steps and not just the final result?
Your example doesn’t work, but you did write “more or less”. This seems to work:
julia> macro my_eval(expr)
:(eval($(Expr(:quote, expr))))
end
@my_eval (macro with 1 method)
julia> a = 1; @my_eval $a
1
Are you looking for @macroexpand
?
@macroexpand @my_eval $a
Yes that is the “more or less” part. QuoteNode
and Expr(:quote)
treats $
differently. This should be mentioned already in the (dev) doc for expression types/heads but I didn’t check to make sure.
Depending on what you mean as the rewriting step and final result, @macroexpand
gives you the result of the macro which include macro hygiene rewrite but not lowering. If you want to see the bare result of the macro without anything else, you can manually call the macro, which is nothing more than a badly named function…
julia> (@eval Base.$(Symbol("@eval")))(LineNumberNode(1), Main, :aaaa)
:(Core.eval(Main, :aaaa))
Do note that you’ll need to be more careful if you want to pass a expression with $
in. The easiest way I can think of now is to actually write a macro that returns the correctly quoted expression for you, i.e.
julia> macro m(expr)
QuoteNode(expr)
end
@m (macro with 1 method)
julia> @m $a
:($(Expr(:$, :a)))
julia> (@eval Base.$(Symbol("@eval")))(LineNumberNode(1), Main, (@m $a))
:(Core.eval(Main, $(Expr(:quote, :($(Expr(:$, :a)))))))
julia> (@eval Base.$(Symbol("@eval")))(LineNumberNode(1), Main, $a)
ERROR: syntax: "$" expression outside quote
(Note the use of QuoteNode
in this case)
Note that you can also just look at the definition directly. https://github.com/JuliaLang/julia/blob/8649af9abed7d628567f68bf280113ce05d76d3f/base/essentials.jl#L178 I find all the methods for @eval
by calling methods
on the function @eval
and if you know the signature you want to call it with you can also use @which
.
julia> @which @eval a
@eval(__source__::LineNumberNode, __module__::Module, ex) in Base at essentials.jl:166
Or
julia> macro call_macro(expr)
@assert expr.head == :macrocall
return :($(esc(expr.args[1]))($(QuoteNode(expr.args[2])), $__module__, $(QuoteNode.(expr.args[3:end])...)))
end
@call_macro (macro with 1 method)
julia> @call_macro @eval a
:(Core.eval(Main, :a))
julia> @call_macro @eval $a
:(Core.eval(Main, $(Expr(:quote, :($(Expr(:$, :a)))))))
julia> eval(@call_macro @eval $a)
ERROR: UndefVarError: a not defined
Stacktrace:
[1] top-level scope at none:0
[2] eval at ./boot.jl:328 [inlined]
[3] eval(::Expr) at ./client.jl:404
[4] top-level scope at none:0
julia> a = 2
2
julia> eval(@call_macro @eval $a)
2
Thanks for the detailed answers. Maybe I’m doing something wrong but methods
doesn’t work with macros so I had to write something like this:
macro methods(expr)
is_macro = isa(expr, Expr) && expr.head == :macrocall
:(methods($(esc(is_macro ? expr.args[1] : expr))))
end
I’ll start reading the developer’s documentation right now. Sorry if I asked about things that were already explained there.