I am going over macros, and decided to test whether quote and colon were identical. See the code below. Apparently they are not. And if not, it means that there likely side effects in rare instances. What am I missing? Thanks.
julia> e = quote 2+3 end
quote
#= none:1 =#
2 + 3
end
julia> dump(e)
Expr
head: Symbol block
args: Array{Any}((2,))
1: LineNumberNode
line: Int64 1
file: Symbol none
2: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 2
3: Int64 3
julia> dump(:(2+3))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 2
3: Int64 3
Edit: The documentation (from typing ?quote at the REPL) also says: “Unlike the other means of quoting, :( ... ), this form introduces QuoteNode elements to the expression tree, which must be considered when directly manipulating the tree. For other purposes, :( ... ) and quote .. end blocks are treated identically.” (I’m not sure exactly when that happens.)
I’ve stumbled on this while creating a variation of the assert macro, and it’s pretty significant for that use case. Apparently the LineNumberNode gets in the way of the system error message showing you the point in the code where the macro is used, and what you get is the macro definition line instead:
Example with colon, showing the code line where the macro is used:
macro assertcolon(ex)
msg = Main.Base.string(ex)
:(
$(esc(ex)) ? $(nothing) : throw(AssertionError($msg))
)
end
function mytestcolon(xx)
@assertcolon 0 == 2 - xx
end
mytestcolon(2)
mytestcolon(1)
Resulting error message:
LoadError: AssertionError: 0 == 2 - xx
in expression starting at /home/nic/src/myassert.jl:12
mytestcolon(xx::Int64) at myassert.jl:9
top-level scope at myassert.jl:12
Now using quote:
macro assertquote(ex)
msg = Main.Base.string(ex)
quote
$(esc(ex)) ? $(nothing) : throw(AssertionError($msg))
end
end
function mytestquote(xx)
@assertquote 0 == 2 - xx
end
mytestquote(2)
mytestquote(1)
The resulting message:
LoadError: AssertionError: 0 == 2 - xx
in expression starting at /home/nic/src/myassert.jl:12
mytestquote(xx::Int64) at myassert.jl:4
top-level scope at myassert.jl:12
As you can see, the message now points straight to the macro line, and retains the top function call, but the actual code line where the macro was called is gone… Gotta use colon to do sneaky code changes, and quote to leave traces. Add this to the list of meta-programming gotchas, I guess!