I don’t quite get why these broadcasting operations do not work:

julia> x = rand(3); y = rand(3);
julia> f(x,y) = @. (x-y > 0.5 ? x : y )
f (generic function with 1 method)
julia> f(x,y)
ERROR: TypeError: non-boolean (BitVector) used in boolean context
Stacktrace:
[1] f(x::Vector{Float64}, y::Vector{Float64})
@ Main ./REPL[38]:1
[2] top-level scope
@ REPL[39]:1

Maaaaaybe. It’s a little stranger than .&& and .||. It (currently) is syntacticly identical to the if statement, and we don’t support if. or .if. Are a .? b : c and a ? b .: c errors? We also don’t support [1,2,3] .: [8,9,10], and I think there’s strange parsing ambiguities around .: in general.

It seems that I have to add a dot to the minus there:

julia> f(x,y) = ifelse.(x - y > 0.5, x, y)
f (generic function with 1 method)
julia> f(x,y)
ERROR: MethodError: no method matching isless(::Float64, ::SVector{3, Float64})

while

julia> f(x,y) = ifelse.(x - y .> 0.5, x, y)
f (generic function with 1 method)
julia> f(x,y)
3-element SVector{3, Float64} with indices SOneTo(3):
-0.636700731442766
0.16919692261464525
-0.3143986886849526

julia> x = @SVector(rand(3)); y = @SVector(rand(3)); period = @SVector(rand(3));
julia> function getdx(x,y,period)
delta = x - y
ifelse.(delta .> 0.5 .* period ? delta - period : delta) .* sign.(x-y)
end
getdx (generic function with 1 method)
julia> getdx(x,y,period)
ERROR: TypeError: non-boolean (SVector{3, Bool}) used in boolean context

I can’t find the possible combination of dots and not-dots or @. that makes this work as a one-liner.

If you want a macro that does this for you, you could always just do something like

julia> using MLStyle
julia> macro (..)(ex)
f = @λ begin
:($a ? $b : $c) => :($Base.ifelse($(f(a)), $(f(b)), $(f(c))))
x => x
end
esc(:($Base.@. $(f(ex))))
end
@.. (macro with 1 method)
julia> let x = rand(3), y = rand(3);
@.. (x-y > 0.5 ? x : y )
end
3-element Vector{Float64}:
0.18240422481232943
0.8391572489988877
0.43624514306257667

This works correctly on nested expressions:

julia> let x = rand(3), y = rand(3);
@.. (x-y > 0.5 ? (x > y ? y : x) : y)
end
3-element Vector{Float64}:
0.7793973727797479
0.3033699172383637
0.625107294185069

But won’t this macro always evaluate both branches?

You can write something like this:

broadcast(xs,ys) do x,y
x-y>0.5 ? x : y
end

where in reality, surely the expressions will be a bit longer: If you don’t want ifelse, there must be some work you’d like not to happen when the condition is not met, so it can’t just be one symbol.

You can also write g(x,y) = @cast _[i] := x[i]-y[i] > 0.5 ? x[i] : y[i] using TensorCast, after someone complained that it didn’t work…

Whereas a ? b : c parses directly to an :if head. Like I said, just little things all around that can change, but the above is what I was referencing. The fact that a && b has its own special parsed form made it significantly easier to achieve a .&& b.