I wanted to create a macro to print simulation status every now and then.
function display_res(ntuple::NamedTuple)
cur = Dates.format(now(), "HH:MM:SS dd.mm.yyyy");
str = savename(ntuple, connector="; ", sort = false)
println("Just finished: $str" * ". Time: $cur");
return nothing;
end
macro display_res(vars...)
quote
display_res($(:(@ntuple($(vars...)))))
end
end
savename and @ntuple are from DrWatson package. The former creates a string, the latter creates NamedTuple (e.g., n=5;g=5;@ntuple(n, g) would give (n=5, g=5).
Above code works in Repl, but when I put it in the module, it doesn’t, resulting in: UndefVarError: n not defined.
Following is the output from @macroexpand:
quote
#= /path_to_file/file.jl:63 =#
MyModuleName.display_res((n = MyModuleName.n, g = MyModuleName.g))
end
So, of course the problem is with MyModuleName.n and MyModuleName.g. Does anybody know how to cope with that? Cheers!
This isn’t really about splatting, it’s just the usual rules about macro hygiene: Metaprogramming · The Julia Language, namely that you need to escape (with esc()) values that you want to interpolate like this.
Here’s a simpler example that works. I’ve removed savename because I don’t have DrWatson, and I’ve also removed @ntuple because you can do the same thing via (; n, g) as of Julia 1.7:
function display_res(ntuple)
println("got: ", ntuple)
end
macro display_res(vars...)
quote
ntuple = (; $(esc.(vars)...))
display_res(ntuple)
end
end
Usage:
julia> function foo()
n = 1
g = 2
@display_res(n, g)
end
foo (generic function with 1 method)
julia> foo()
got: (n = 1, g = 2)
As @rdeits underlined, recent versions of Julia make it more easy to build named tuples and keyword arguments. So if you do not need to support older Julia versions, you might want to consider getting rid of macros entirely. Something like that (again removing savename to make the example runnable without DrWatson):
function display_res(; kwargs...)
ntuple = NamedTuple(kwargs)
cur = Dates.format(now(), "HH:MM:SS dd.mm.yyyy");
str = string(ntuple)
println("Just finished: $str. Time: $cur");
return nothing;
end
julia> let
a = 2
b = 3
display_res_new(; a, b)
end
Just finished: (a = 2, b = 3). Time: 10:47:37 12.03.2022
The call syntax is perhaps a bit less user-friendly than what you wanted, but this way you don’t have to worry about topics like macro hygiene (which are arguably kind of hard to grasp).