@. x-y? x : y broadcasting does not work?

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
 [1] f(x::Vector{Float64}, y::Vector{Float64})
   @ Main ./REPL[38]:1
 [2] top-level scope
   @ REPL[39]:1

Because it’s not supported. It’s the same as trying to do

@. if x-y > 0.5

You can use @. ifelse(x-y > 0.5, x, y) instead.


This could be supported in the same way that .&& and .|| support was added, but it just has not yet been attempted as far as I’m aware.

1 Like

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.

1 Like

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})


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):

Yeah, sorry, I should’ve just @.'ed the entire expression. Edited above.


Actually what I was trying to do is this:

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)
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.

1 Like

Change your ? and : to commas.

a && b was syntactically identical to

if a; b end


if !(a); b end

and nobody was arguing in that pr that it meant that we had to add support for if.

That said, yeah expressions like a .? b .: c are pretty ugly

1 Like

:flushed: :flushed: :flushed:

oh, sorry, I think changed from one to the other syntax so many times that I got lost

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
           esc(:($Base.@. $(f(ex))))
@.. (macro with 1 method)

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

This works correctly on nested expressions:

julia> let  x = rand(3), y = rand(3);
           @.. (x-y > 0.5 ? (x > y ? y : x) : y)
3-element Vector{Float64}:
1 Like

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

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…


It’s superficial, but they were and are syntactically different:

julia> Meta.show_sexpr(:(a && b))
(:&&, :a, :b)
julia> Meta.show_sexpr(:(if a; b; end))
(:if, :a, (:block,
    :(#= REPL[7]:1 =#),

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.


Aha, I see what you mean now. Thanks!

1 Like