# Bools in arithmetic operations

Bools seem to get converted/promoted to numerical zero/one:

``````julia> false + 42
42

julia> true + 42
43

julia> false + 3.14
3.14

julia> true + 3.14
4.140000000000001

julia> false + complex(1, 2)
1 + 2im

julia> true + complex(1, 2)
2 + 2im
``````

I don’t like it. For example,

``````julia> gety(m, index_offset=0) = m[:, index_offset+2]
gety (generic function with 2 methods)

julia> m=[1 3 5 8;2 11 9 6]
2×4 Array{Int64,2}:
1   3  5  8
2  11  9  6
``````

was accidently called as

``````julia> gety(m, true)
2-element Array{Int64,1}:
5
9
``````

The bug was difficult to hunt down.

Is it possible to optionally suppress such automatic conversions in arithmetic operators?

For the general question of "why is `Bool` an `Integer`", see:

For your particular case, if you don’t want `Bool` to be accepted as input in your function, just make it throw an error in such a case:

``````julia> gety(m, ::Bool) = throw(ArgumentError("Bool not allowed"))
gety (generic function with 1 method)

julia> gety(m, true)
ERROR: ArgumentError: Bool not allowed
Stacktrace:
[1] gety(::Array{Int64,1}, ::Bool) at .\REPL[1]:1
[2] top-level scope at REPL[2]:1234:1
``````
6 Likes

Bools are neither converted nor promoted to numerical zero/one, they are numerical zero/one.

``````julia> supertype(Bool)
Integer
``````
4 Likes

Thanks for the link. It shows that this is a complicated issue, which I had already suspected.

In reality I have of course many candidate functions which should not work with Bool arguments. For now I restrict them like

``````julia> getty(m, index_offset::Int=0) = m[:, index_offset+2]
getty (generic function with 2 methods)
``````

which is more restrictive than needed but easier to exclude Bool arguments.

Dispatching on only `Int` is too restrictive. Generic code is normally preferable, so it’s better to define special behavior only for the exceptional cases you want to rule out (`Bool` input, in this case).

But if you don’t want to write the extra methods to manage those exceptions, at least I recommend you to allow any other `Integer` that is not `Bool`. `Bool` happens to be the only subtype of `Integer` which is not either `Signed` or `Unsigned`, so you could write:

``````getty(m, index_offset::Union{Signed, Unsigned}=0) = m[:, index_offset+2]
``````

While restricting the input is an option to catch the error at that point, it seems that the bug was probably before the call to your function, somewhere where a `Bool` was generated where an integer was expected to be generated. Perhaps the proper place to add an error message or deal with the Bool was at that point, if for some reason that turns out to be possible and the program is expected to stop if that occurs.

(otherwise, although you can prevent that error from propagating in your own functions, it will propagate in every other functions of other packages or Base that accept Bools as integers, which are probably most of them)

6 Likes

Index inputs are converted to `Int` by the `to_indices` function. One generic way of doing this, in that case is

``````gety(m, index_offset::Int=0) = m[:, index_offset+2]
gety(m, index_offset) = gety(m, to_indices(m, (index_offset,))...)
``````

This should accept all the same types of indices that other Julia arrays do, staying up-to-date with whatever `to_indices` does. Specifically, `Bool`s will be disallowed, while other integers are accepted.

(Please be aware, I’ve never had reason to use `to_indices` before, so double check that it works as expected.)

1 Like

Thanks for the replies, there are several soulutions to my problem. I add now

``````const ExceptBool = Union{Signed, Unsigned}
``````

to my relevant modules (or perhaps even to `startup.jl`), and use it in functions like the above mentioned example

``````gety(m, index_offset::ExceptBool=0) = m[:, index_offset+2]
``````

to enforce that using Bools for arguments in such functions must be explicit:

``````julia> gety(m, true)
ERROR: MethodError: no method matching gety(::Matrix{Int64}, ::Bool)
Closest candidates are:
gety(::Any) at REPL[10]:1
gety(::Any, ::Union{Signed, Unsigned}) at REPL[10]:1
Stacktrace:
[1] top-level scope

julia> gety(m, Int(true))
2-element Vector{Int64}:
5
9
``````

Alternatively, you can just do

``````@assert !(index_offset isa Bool)
``````

inside functions. It will be costless if your types are inferred.

9 Likes