Skip to content

Three-Valued Logic and the World of Issomething #29665

Closed
@dalum

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    missing dataBase.missing and related functionality

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions