Broadcasting in 0.7

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?

1 Like

Looking at this again (I do not think there have been solutions to issue #27988), I really think the issue is that broadcasting with dot notation was the only mechanism to hook into the = syntax with fused, non-allocating arithmetic operations for arbitrary types. Is this something that is even technically possible, at present or in the future?