I am trying using macros. Here is what I have
using MacroTools: postwalk
_isoperator(s::Symbol) = s ∈ (:+, :-, :*, :/, :.+, :.-, :.*, :./)
_isoperand(::Union{Expr, Number, QuoteNode}) = false
_isoperand(s::Symbol) = !_isoperator(s)
macro sq(expr)
expr_state = postwalk(s->_isoperand(s) ? :(Flows._state($s)) : s, expr)
expr_quad = postwalk(s->_isoperand(s) ? :(Flows._quad($s)) : s, expr)
quote
if typeof($(esc(expr.args[1]))) <: AugmentedState
$(esc(expr_state))
$(esc(expr_quad))
else
$(esc(expr))
end
$(esc(expr.args[1]))
end
end
Usage demo (discard the Flows
stuff. It’s the module the package where the macro is defined):
julia> @macroexpand @sq x .= x .+ Δt./6.0.*(k1 .+ 2.0.*k2 .+ 2.0.*k3 .+ k4)
quote
#= REPL[5]:5 =#
if (Main.typeof)(x) <: Main.AugmentedState
#= REPL[5]:6 =#
Flows._state(x) .= Flows._state(x) .+ (Flows._state(Δt) ./ 6.0) .* (((Flows._state(k1) .+ 2.0 .* Flows._state(k2)) .+ 2.0 .* Flows._state(k3)) .+ Flows._state(k4))
#= REPL[5]:7 =#
Flows._quad(x) .= Flows._quad(x) .+ (Flows._quad(Δt) ./ 6.0) .* (((Flows._quad(k1) .+ 2.0 .* Flows._quad(k2)) .+ 2.0 .* Flows._quad(k3)) .+ Flows._quad(k4))
else
#= REPL[5]:9 =#
x .= x .+ (Δt ./ 6.0) .* (((k1 .+ 2.0 .* k2) .+ 2.0 .* k3) .+ k4)
end
#= REPL[5]:11 =#
x
end
This work well, with the exception that one cannot use in-place assignment operators (is this the right name?) like .+=
and others and needs to be explicit, as in the demo. Not a big deal, though, as this is only used in the package code, not by user code. Comments welcome.
One question. Assuming this is used within a type-stable function where the type of x
is well defined. Is the compiler able to elide the branch?