Three-Valued Logic and the World of Issomething #29665
Description
I couldn't find an issue discussing this, so now I'm opening one.
Right now, iszero(missing)
returns missing
. But there is a lot of code in base (at least in LinearAlgebra
) that relies on the assumption that iszero
will return a Boolean value. For instance, matrix inversion will do such a check at several stages, and error with a SingularException
if iszero
returns true [1].
In most of the uses I have seen, iszero(x)
is used to branch or short–circuit a function in the case where x
is zero with certainty, i. e., iszero(x) === true
. If x
does not have this property, then the generic algorithm can proceed. For instance, I think most would assume that an innocuous looking generic function like: strongdiv(a, x) = iszero(a) && iszero(x) ? 0. : a/x
, should just work, as long as zero(a)
is a well–defined concept.
At present, however, it won't. It will fail when given a missing
, although a/missing
is a perfectly acceptable thing to do.
My own use case, which prompted me to consider this, involves a type that wraps a symbol, expression or number. I. e., a symbolic type for doing symbolic manipulations. In this case, a Symbolic(:a)
is not known to be zero(Symbolic)
, and so a symbolic a == 0
just returns a wrapped expression object. Right now, the default fallback in Base is iszero(x) = x == zero(x)
, which means that iszero(a)
would return the wrapped expression object, a == 0
, and not a Boolean value. Thus in order for my user–defined type to support the generic pattern above, iszero(::Symbolic) = false
must be implemented.
This is of course not a major issue, it is probably only a few lines of code in most cases, but it raises the question: if almost all code assumes that iszero
returns a Bool
, should the default fallback return a Bool
? And should this be enforced through documentation?
The argument above extends to the whole issiverse of isone
, isinf
, isfinite
, isodd
, iseven
, isreal
, isnan
, and any is
-function that I've missed.
My proposal is that they should, i. e., that iszero(x) = (x == zero(x)) === true
, and that the methods for missing
should be updated accordingly. This would make a clear distinction between ==
comparison and is
-functions, and give the latter a more reliable behaviour in logical contexts. The odd-one-out here is of course isless
, which treats missing
as larger than any other value, but that can be seen as an exception to a more general rule about certainty to preserve its use in ordering.
TL:DR; I propose that it should be the case that most functions starting with is
reliably return a Bool
, while ==
comparisons are allowed to return other objects, such as missing
.
[1] (For a matrix with missing
in it, the generic algorithm for matrix inverses with missing
values presently fails much earlier because it wrongly tries to promote to Float64
, but that is a tangential issue.)
Activity