# Is broadcast possible over ternary expressions?

**URL:** <https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518>\
**Category:** Internals & Design\
**Created:** [July 22, 2020, 9:32pm UTC](https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518 "2020-07-22T21:32:05Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)\
**Post date:** [July 22, 2020, 9:32pm UTC](https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518/1 "2020-07-22T21:32:05Z")

</div>

I would love to be able to do something like the following:

```julia
@. l < u ? f!(x) : g!(x)

```

I.e.

```julia
[li<ui ? f!(xi) : g!(xi) for (li, ui, xi) in zip(l, u, x)]

```

However, expanding the first line, it’s clear that it won’t be equivalent to the second, due to there being nothing like `?.` and `:.`. (the whole thing is directly parsed as one if-else statement)

```julia
julia> @macroexpand @. l < u ? f!(x) : g!(x)
:(if l .< u
      f!.(x)
  else
      g!.(x)
  end)

```

As a trivial example of what happens when you try the above:

```julia
julia> @. [-1] < [1] ? [0] : [1]
ERROR: TypeError: non-boolean (BitArray{1}) used in boolean context

```

Using `ifelse` will broadcast correctly, but because it evaluates both arguments ahead of time, it cannot be used exactly how I would like, since the functions `f!` and `g!` will both be applied (admittedly a niche limitation).

I’m assuming there isn’t already a way of doing what I’d like in a simple expression, but if there is, I’d appreciate a pointer. If indeed there isn’t, could it be possible to allow something like this in the language? Assuming the mechanism for turning ternary expressions into if-else statements stays the same, then presumably this would require defining broadcast behavior over if-else statements. It seems to me that this could at least be done with `@.` (it could modify the expression as a special case) but not easily (or at all?) with the regular `broadcast` approach, since my understanding is that it applies only to functions.

Interested to hear others’ thoughts on this.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [July 22, 2020, 9:46pm UTC](https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518/2 "2020-07-22T21:46:19Z")

</div>

You can do this with `ifelse` because that evaluates it’s arguments, and can’t short circuit the way ternaries can.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [July 22, 2020, 9:50pm UTC](https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518/3 "2020-07-22T21:50:46Z")

</div>

Yeah, there’s not such a construct AFAIK. In general broadcast does not apply to control flow, which as you note, `?:` is just a shorthand for `if ... else ... end`. I’d be very hesitant to introduce such a feature.

---

<div class="post-metadata">

**Author:** ![tbeason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbeason/32/15898_2.png) [@tbeason](https://discourse.julialang.org/u/tbeason)\
**Post date:** [July 22, 2020, 10:13pm UTC](https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518/4 "2020-07-22T22:13:11Z")

</div>

In this case could you just write your own function that works for a single value/comparison and then broadcast that?

---

<div class="post-metadata">

**Author:** ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)\
**Post date:** [July 22, 2020, 10:27pm UTC](https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518/5 "2020-07-22T22:27:26Z")

</div>

> Yeah, there’s not such a construct AFAIK. In general broadcast does not apply to control flow

Yes, it would certainly have to modify control flow, turning one if-else into something like a generator or map with if-else statements. I’m not familiar with the broadcast machinery that makes differently indexible object all work together nicely, but the idea would be, in addition to propagating `broadcast` to subexpressions in an `@. if ...` statement, also converting the top level expression into something amenable to iteration. The comprehension in the original post was a poor imitation.

> In this case could you just write your own function that works for a single value/comparison and then broadcast that?

Yes, in this case it would be simple enough to do:

```julia
h!(li, ui, xi) = li<ui ? f!(xi) : g!(xi)
h!.(l, u, x)

```

In my “real” use case, it’s much uglier to do it this way than if the single ternary expression were possible. Of course all of this is about aesthetics! 😛

> You can do this with `ifelse` because that evaluates it’s arguments, and can’t short circuit the way ternaries can.

Unfortunately, if `f!` and `g!` have some effect on `xᵢ ` (or otherwise), applying both of them in the `ifelse` will be different than only the applying the “correct” one in an `if-else`.

---

<div class="post-metadata">

**Author:** ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)\
**Post date:** [July 23, 2020, 1:51pm UTC](https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518/6 "2020-07-23T13:51:19Z")

</div>

Looking at the ` __dot__ ` macro, it looks like some blocks are already special cased.  
E.g. `@. for` doesn’t distribute the broadcast to the `for i = 1:n`.  
Since a for-loop is also control-flow, perhaps this is less extreme than it first sounded? I still don’t know what the if-else expression would have to change to to work as expected, but whatever that may be, I guess it could happen under `elseif x.head == :if`

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [July 23, 2020, 2:33pm UTC](https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518/7 "2020-07-23T14:33:12Z")

</div>

> [@tomerarnon](#):
>
> Since a for-loop is also control-flow, perhaps this is less extreme than it first sounded?

That’s funny, I have exactly the opposite takeaway. Note that `@. for ` doesn’t actually embed the loop inside the broadcast — it ignores it and keeps it as outer control flow! That is:

```Julia
julia> x = 1:4
1:4

julia> @. for i in 2:3
           println(x^i)
       end
1
4
9
16
1
8
27
64

```

The semantics you want would (I think) print 1, 1, 4, 8, 9, 27, 16, 64 and would return a vector of `nothing`s.

---

<div class="post-metadata">

**Author:** ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)\
**Post date:** [July 23, 2020, 7:27pm UTC](https://discourse.julialang.org/t/is-broadcast-possible-over-ternary-expressions/43518/8 "2020-07-23T19:27:27Z")

</div>

Hmm, yes I see your point. I suppose it boils down to what the intuition is behind the syntax:

```julia
@. if l < u
    x1
else
    x2
end

```

Ignoring the non-scalar `l/u` case (non-boolean error), the above is equivalent to

```julia
if l < u
   @. x1
else
   @. x2
end

```

I see your point that this is in line with the approach to `for`, but I still think it’s missing out on something more useful, which is broadcasting over the entire conditional 😉
