X === nothing vs x isa Nothing

Which one is preferred? Why?

I think x == nothing works fine in new versions of Julia.

Imo, `isnothing(x) looks cleanest.

7 Likes

The problem with isnothing is it does not exist in Julia 1.0 unless you are using Compat.jl.

That’s true, but 1.6 will very likely be the LTS within 1 month, so 1.0 is not that relevant.

4 Likes

Use ===
https://docs.julialang.org/en/v1/manual/performance-tips/#Checking-for-equality-with-a-singleton

2 Likes

I would say that x === nothing and x isa Nothing are the same. In both cases, type inference is enough to determine whether the statement is true. (I personally find === easier to read.)

isnothing(x) has the advantage of allowing others to create types that are equivalent to Nothing to your code. (This might be useful for things like autodifferentiation, debugging, etc.)

x == nothing also has that advantage, but it makes type inference (edit: I mean dispatch) harder, because the == function takes two arguments.

2 Likes

As of 1.7, that’s no longer true

Can you explain it, please? Why is it not true in 1.7 and what should be used instead?

A few comments:

=== is preferred to == as per doc here as @baggepinnen pointed out.

isnothing simply uses ====

I saw both x === nothing and x isa Nothing in the Julia base source code.

Yes, both are fine. Use whichever you prefer.

Personally I like === as it brings back fond memories of Lisp (eq), but that’s a style preference, the language does not care. In a situation with

abstract type NonResult end
struct NonConvergence <: NonResult end # only one subtype, but I may refactor later
const non_convergence = NonConvergence()

I would use isa NonResult.

1 Like

It is perhaps (just about) slightly interesting to look at the performance.

Among x === nothing, x isa Nothing and isnothing(x), === seems to be the fastest and isnothing() the slowest (timing).

This makes sense because checking for identity (=== pointer comparison) is quicker than checking a type, for the type has to be looked up in an internal table which causes a slight overhead. the function call is slower still for clear reasons.

This β€œanalysis” is not worth much though, because firstly isnothing() is slightly different than the other two as it can be extended, while the other two are close enough that in a real program this would not make any difference. As such, I guess performance comparisons on this are little more than academic interest.

// Edit
if the object being compared is actually nothing, then isnothing() is much faster and about the same time of isa Nothing. Is isnothing(x::Nothing) perhaps inlined to just return true? It woud make sense and explains why it is just the same time as a type check.

On Julia master branch (to be Julia 1.7) at the moment,

isnothing(x) = x === nothing

Previously (Julia 1.1 to 1.6, inclusive) it was

isnothing(::Any) = false
isnothing(::Nothing) = true
1 Like
julia> @which 5 == nothing
==(x, y) in Base at Base.jl:87

==(x, y) = x === y

julia> @which 5 === nothing
ERROR: ArgumentError: argument is not a generic function

So they are all the same unless there’s a better method for the other operand, in which case that should probably be used. I don’t think there’s any particular reason to use === for nothing, is there?.

the @which error is just due to the macro @which. It stems from the fact that === is specialized by the parser why == is not (or, less specialized).

2 Likes