For probabilistic programming work, I was exploring the idea of adding @goto and @label statements in the code for a model, and then calling to an outside “driver” function to see where to go next. Prototyping this idea, I need the driver to return a Command, with Goto as one option. So something like
abstract type Command end
struct Goto <: Command
target::Symbol
end
Since @goto is implemented as
macro goto(name::Symbol)
return esc(Expr(:symbolicgoto, name))
end
I thought I could do something like
macro maygoto(ex)
quote
command = $ex
result = if command isa Goto
target = command.target
esc(Expr(:symbolicgoto, target))
else
nothing
end
result
end
end
But a test run doesn’t work:
julia> function f()
println("start")
@maygoto Goto(:blah)
println("skip this")
@label blah
println("done")
end
f (generic function with 1 method)
julia> f()
start
skip this
done
Am I making a silly mistake here? Or maybe just a very serious mistake? Help please
I don’t think you can truly test for if command is a Goto inside macro since type is a run-time concept but macro only sees source code:
julia> macro maygoto(ex)
command = ex.args[1]
if command == :Goto
return esc(Expr(:symbolicgoto, ex.args[2].value))
else
return nothing
end
end
@maygoto (macro with 1 method)
julia> function f()
println("start")
@maygoto Goto(:blah)
println("skip this")
@label blah
println("done")
end
f (generic function with 1 method)
julia> f()
start
done
julia> macro maygoto(ex)
quote
command = $ex
result = if command isa Goto
println("It's a goto!")
target = command.target
esc(Expr(:symbolicgoto, target))
else
nothing
end
result
end
end
@maygoto (macro with 1 method)
julia> function f()
println("start")
target = :blah
@maygoto Goto(:blah)
println("skip this")
@label blah
println("done")
end
f (generic function with 1 method)
julia> f()
start
It's a goto!
skip this
done
right, thus the whole quote end is the result of expansion and you get a perfectly correct isa Goto at run-time but the returned Expr() is “useless” now that it’s run-time
When I was learning macros, someone pointed out that they don’t give you any new capabilities, but just let you write things with fewer keystrokes. That made a lot of sense, and really helped me get more comfortable with them.
goto is a really low-level thing if you think about it, it literally marks a location to jump to on stack. So the reason it’s a macro is that it simply leaves an expression for the compiler to figure out what is the “location” on the stack to jump to once it compiles the function into binary (before then, you can only talk about “label” because you don’t know how much big the binary blob of function ends up becoming).
Looking back, even in C++ the label/goto has weird syntax, because they kind of stay location markers until the last step (asm):