Hello community. I’ve just spent hours confusingly revolving around the following quirk.
Given this code in my package:
macro a()
println("expand a")
:(println("exec a"))
end
macro b()
println("expand b")
:(println("exec b"))
end
And this user code:
println("invoke a")
@a
println("invoke b")
@b
I expect the following behaviour (based on the current REPL behaviour):
invoke a
expand a
exec a
invoke b
expand b
exec b
However, this is what happens when I automate the test:
using Test
@testset "" begin
println("invoke a")
@a
println("invoke b")
@b
end
expand a # !
expand b # !
invoke a
exec a
invoke b
exec b
Is that expected behaviour?
Is that somehow configurable?
Can I rely on the REPL behaviour (i.e. macros being expanded on invocation) and just work around @testset for my tests, or should I not assume that Julia upholds any guarantee regarding macros expansion time?
Yes, macros are expanded before the expanded code is executed. They are expanded outside-in (you can check with Base.@macroexpand1, if I’m not mistaken).
No. Macros are an AST transform.
I’m not sure I follow - when a macro is expanded, the code inside the macro building the expression (even if not contributing to the returned expression) still has to run.
It’s not particular to @testset, it’s because a begin block is a compound expression. The REPL reads one complete expression, not line, at a time; the begin block is only considered complete at the end.
julia> begin
println("invoke a")
@a
println("invoke b")
@b
end
expand a
expand b
invoke a
exec a
invoke b
exec b
Thank you for feedback @Sukera@Benny IIUC what I’m observing is rather related to how julia parser / expansion works, and also happens without @testset with plain begin/end blocks.
I guess I just need to be careful not to rely on the exact nesting order between my macro expansions and actual generated code execution, because I cannot enforce that they won’t be invoked within macros or begin/end blocks downstream ^ ^"