ERROR: TypeError: non-boolean (Missing) used in boolean context

Why does this happen?

julia> 3 < missing < 5
ERROR: TypeError: non-boolean (Missing) used in boolean context

Is it expanding to this?

julia> (3 < missing) && (missing < 5)
ERROR: TypeError: non-boolean (Missing) used in boolean context

This one works

julia> (3 < missing) & (missing < 5)
missing

Would it make sense to change it so that this works?

You right, the error is occurring because operations on Missing (such as comparisons in this case) results in missing, and using a type other than Bool with boolean operators like && results in an error. So essentially, the expression: 3 < missing < 5 is expanded to (3 < missing) && (missing < 5), which expands to missing && missing, and that’s what results in the error.

The reason that (3 < missing) & (missing < 5) works its because & is the bitwise and operator, which is not equal to the logical and operator (&&). So essentially, using operations like && and || with objects that are not Boolean results in a error, and performing any operation (other than boolean operations) on missing will, generally, result in missing.

1 Like

I think the documentation is clear:

help?> &&
search: &&

  x && y

  Short-circuiting boolean AND.

help?> &
search: & &&

  x & y

  Bitwise and. Implements three-valued logic
  (https://en.wikipedia.org/wiki/Three-valued_logic), returning missing if one
  operand is missing and the other is true. Add parentheses for function application
  form: (&)(x, y).

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> 4 & 10
  0
  
  julia> 4 & 12
  4
  
  julia> true & missing
  missing
  
  julia> false & missing
  false

Chained comparisons use the && operator for scalar comparisons, and the & operator for elementwise comparisons, which allows them to work on arrays. For example, 0 .< A .< 1 gives a boolean array whose entries are true where the corresponding elements of A are between 0 and 1.

https://docs.julialang.org/en/v1/manual/mathematical-operations/#Chaining-comparisons

What is the advantage of && for this? Why not just use & in both cases? To handle a missing I can’t use chaining, because it fails with &&, but it would work fine with &.

uhh, if you want an easy way you can just use the broadcast:

julia> 1 .< missing .< 5
missing

julia> 1 .< 2 .< 5
true

As numbers are containers of themselves this works.

The advantage is being what is expected, I believe. At least, I expected 1 < x < 5 to be expanded to (1 < x) && (x < 5). & is for bitwise comparison so it would be unexpected to see it there. What is a little strange to me is that even with && being short-circuiting the double constraint is not.

julia> 1 < 2 < error("a")
ERROR: a
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] top-level scope at REPL[4]:1

I think this is a footgun. It’s not obvious that 1 < missing < 2 lowers to (1 < missing) && (missing < 2). I get the appeal of short circuiting in this instance, but it’s still unexpected for new users.

It’s frustrating that we have &&, which errors with missing and & which has unexpected behavior with integers etc, but nothing which is just for booleans and handles missing.