Internals of assignment when doing short circuit evaluation

I am doing short circuit evaluation as an if then statement. But I am unsure of how it works so that, even if the second term of the “and” operation is not a boolean value, it works and it returns the value (it executes it instead of evaluating it to its true value:

true && (n==2)

Would be a normal use, but in

true && (n=2)

I wonder how it works. I knowthe parentheses are used to indicate precedence of the assignment

a && b is equivalent to a ? b : false: if a is true it returns the value of b regardless of whether b is boolean, and if a is false it returns false.

So, true && (n=2) is equivalent to n=2 (which returns the right-hand side, 2).

Of course, this will lead to an error if you try to use the result of the && as a boolean value:

julia> if true && (n=2)
          println("Huh?")
       end
ERROR: TypeError: non-boolean (Int64) used in boolean context

The documentation for && should really be clarified on this point: clarify short-circuit && and || docs by stevengj · Pull Request #56420 · JuliaLang/julia · GitHub

2 Likes

Okay, so is it implemented like a?b: false ? Thank you for clarifying. a secondary doubt is, okay, it “returns” (n=2), but it is not a value, but a code. It returns a code to be evaluated. How does Julia handle passing code as a value to be returned and how does it know it needs to evaluate it?

Assignment expressions have a value equal to their right-hand-side in Julia. See Assignment expressions in the manual.

Thank you. My question goes now to the || operator. In that case, it also works with non boolean values. How is that implemented? Is it possible to see how is it implemented by myself?

You can see what it lowers to using Meta.@lower, e.g.

julia> Meta.@lower true && n == 2
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─      goto #3 if not true
2 ─ %2 = ==
│   %3 = (%2)(n, 2)
└──      return %3
3 ─      return false
))))

julia> Meta.@lower true || n == 2
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = true
└──      goto #3 if not %1
2 ─      return %1
3 ─ %4 = ==
│   %5 = (%4)(n, 2)
└──      return %5
))))

Similar: x || y is equivalent to x ? true : y.

Personally I’m not a fan of short-circuit for one-line if. Early on with Julia, it became fashionable, perhaps because it occurs a lot in Base and the documentation even advocated for it. Thankfully less recently.

To me a huge selling point of Julia is that it’s readable to non-users almost as pseudo-code. But those unfamiliar with short-circuiting may just go “huh?”

It’s admittedly way less cool to do

if a; b; end # semi-colons unnecessary but easier to parse

but also clearer. If we wanted to be cool there are all sorts of incredible tricks possible with for in C, so why even try with Julia?

I imagine even experienced programmers can occasionally brain-freeze and accidentally mix up || and &&.

1 Like

It’s an idiom that pre-dates Julia — it’s common in C, and also in sh scripting.