Note that this doesn’t work at all within a function:
julia> function f(subtotal)
@with subtotal begin
price + shipping
end
end
f (generic function with 1 method)
julia> f(subtotal)
ERROR: MethodError: no method matching (::Base.Meta.var"#14#16")(; price=12.99, shipping=3.99)
Closest candidates are:
#14(; price, shipping) at REPL[1]:16
Stacktrace:
[1] macro expansion at ./REPL[1]:9 [inlined]
[2] f(::NamedTuple{(:price, :shipping),Tuple{Float64,Float64}}) at ./REPL[8]:2
[3] top-level scope at REPL[9]:1
and for the same reason it cannot be benchmarked with @btime
from BenchmarkTools.
For a much simpler solution which works in local and global scope and imposes no performance penalty at all, see: With struct do - #12 by rdeits
For example:
julia> using MacroTools
julia> using MacroTools: postwalk
julia> macro with(container, expr)
esc(postwalk(expr) do ex
if @capture(ex, &field_)
:(($container).$field)
else
ex
end
end)
end
@with (macro with 1 method)
julia> subtotal = (price=12.99, shipping=3.99)
(price = 12.99, shipping = 3.99)
julia> @with subtotal begin
&price + &shipping
end
16.98
julia> function f(subtotal)
@with subtotal begin
&price + &shipping
end
end
f (generic function with 1 method)
julia> f(subtotal)
16.98
julia> using BenchmarkTools
julia> @btime f($subtotal)
0.029 ns (0 allocations: 0 bytes)
16.98
Note that the suspiciously-fast benchmark time of < 1ns means that the compiler has actually eliminated all of the code in the benchmark and just replaced it with the answer. That’s actually great news for us–it means that the revised @with
macro doesn’t make the code any harder for the compiler to analyze and optimize.