# X === nothing vs x isa Nothing

**URL:** <https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546>\
**Category:** General Usage\
**Created:** [July 13, 2021, 12:53am UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546 "2021-07-13T00:53:54Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![dpkwhan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dpkwhan/32/26456_2.png) [@dpkwhan](https://discourse.julialang.org/u/dpkwhan)\
**Post date:** [July 13, 2021, 12:53am UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/1 "2021-07-13T00:53:54Z")

</div>

Which one is preferred? Why?

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [July 13, 2021, 1:05am UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/2 "2021-07-13T01:05:48Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [July 13, 2021, 1:17am UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/3 "2021-07-13T01:17:21Z")

</div>

Imo, `isnothing(x) looks cleanest.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [July 13, 2021, 5:13am UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/4 "2021-07-13T05:13:06Z")

</div>

The problem with `isnothing` is it does not exist in Julia 1.0 unless you are using [Compat.jl](https://github.com/JuliaLang/Compat.jl/blob/78bba1021ad387ca361ccbe0c3491ea3b08bb024/src/Compat.jl).

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [July 13, 2021, 5:14am UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/5 "2021-07-13T05:14:28Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [July 13, 2021, 6:29am UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/6 "2021-07-13T06:29:51Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [July 13, 2021, 7:53am UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/7 "2021-07-13T07:53:14Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [July 13, 2021, 12:21pm UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/8 "2021-07-13T12:21:52Z")

</div>

As of 1.7, that’s no longer true

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [July 13, 2021, 12:36pm UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/9 "2021-07-13T12:36:20Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![dpkwhan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dpkwhan/32/26456_2.png) [@dpkwhan](https://discourse.julialang.org/u/dpkwhan)\
**Post date:** [July 13, 2021, 12:52pm UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/10 "2021-07-13T12:52:47Z")

</div>

A few comments:

=== is preferred to == as per doc [here](https://docs.julialang.org/en/v1/manual/performance-tips/#Checking-for-equality-with-a-singleton) as @baggepinnen pointed out.

`isnothing` simply uses ====

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

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [July 13, 2021, 1:00pm UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/11 "2021-07-13T13:00:05Z")

</div>

> [@dpkwhan](#):
>
> saw both

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

```julia
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`.

---

<div class="post-metadata">

**Author:** ![ess3sq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ess3sq/32/26834_2.png) [@ess3sq](https://discourse.julialang.org/u/ess3sq)\
**Post date:** [July 13, 2021, 1:22pm UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/12 "2021-07-13T13:22:17Z")

</div>

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](https://replit.com/@ess3sq/timenothing)).

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.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [July 14, 2021, 9:14pm UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/13 "2021-07-14T21:14:33Z")

</div>

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

```julia
isnothing(x) = x === nothing

```

Previously (Julia 1.1 to 1.6, inclusive) it was

```julia
isnothing(::Any) = false
isnothing(::Nothing) = true

```

> <https://github.com/JuliaLang/julia/pull/38905>
>
> This PR propagates \`Conditional\`s inter-procedurally when a
> \`Conditional\` at re…turn site imposes a constraint on the call arguments.
> When inference exits local frame and the return type is annotated as
> \`Conditional\`, it will be converted into \`InterConditional\` object,
> which is implemented in \`Core\` and can be directly put into the global
> cache. Finally after going back to caller frame, \`InterConditional\` will
> be re-converted into \`Conditional\` in the context of the caller frame.
> 
> ## improvements 
>  
> So now some simple "is-wrapper" functions will propagate its constraint
> as expected, e.g.:
> \`\`\`julia
> isaint(a) = isa(a, Int)
> @test Base.return\_types((Any,)) do a
> isaint(a) && return a # a::Int
> return 0
> end == Any\[Int\]
> 
> isaint2(::Any) = false
> isaint2(::Int) = true
> @test Base.return\_types((Any,)) do a
> isaint2(a) && return a # a::Int
> return 0
> end == Any\[Int\]
> 
> function isa\_int\_or\_float64(a)
> isa(a, Int) && return true
> isa(a, Float64) && return true
> return false
> end
> @test Base.return\_types((Any,)) do a
> isa\_int\_or\_float64(a) && return a # a::Union{Float64,Int}
> 0
> end == Any\[Union{Float64,Int}\]
> \`\`\`
> 
> (and now we don't need something like #38636)
> 
> ## benchmarks
> 
> A compile time comparison:
> \> on the current master (82d79ce18f88923c14d322b70699da43a72e6b32)
> \`\`\`
> Sysimage built. Summary:
> Total ─────── 55.295376 seconds
> Base: ─────── 23.359226 seconds 42.2444%
> Stdlibs: ──── 31.934773 seconds 57.7531%
> JULIA usr/lib/julia/sys-o.a
> Generating REPL precompile statements... 29/29
> Executing precompile statements... 1283/1283
> Precompilation complete. Summary:
> Total ─────── 91.129162 seconds
> Generation ── 68.800937 seconds 75.4983%
> Execution ─── 22.328225 seconds 24.5017%
> LINK usr/lib/julia/sys.dylib
> \`\`\`
> 
> \> on this PR (37e279bce7136e48f159811641b68143412c3881)
> \`\`\`
> Sysimage built. Summary:
> Total ─────── 51.694730 seconds
> Base: ─────── 21.943914 seconds 42.449%
> Stdlibs: ──── 29.748987 seconds 57.5474%
> JULIA usr/lib/julia/sys-o.a
> Generating REPL precompile statements... 29/29
> Executing precompile statements... 1357/1357
> Precompilation complete. Summary:
> Total ─────── 88.956226 seconds
> Generation ── 67.077710 seconds 75.4053%
> Execution ─── 21.878515 seconds 24.5947%
> LINK usr/lib/julia/sys.dylib
> \`\`\`
> 
> Here is a sample code that benefits from this PR:
> \`\`\`julia
> function summer(ary)
> r = 0
> for a in ary
> if ispositive(a)
> r += a
> end
> end
> r
> end
> 
> ispositive(a) = isa(a, Int) && a \> 0
> 
> ary = Any\[\]
> for \_ in 1:100\_000
> if rand(Bool)
> push!(ary, rand(-100:100))
> elseif rand(Bool)
> push!(ary, rand('a':'z'))
> else
> push!(ary, nothing)
> end
> end
> 
> using BenchmarkTools
> @btime summer($(ary))
> \`\`\`
> 
> \> on the current master (82d79ce18f88923c14d322b70699da43a72e6b32)
> \`\`\`
> ❯ julia summer.jl
> 1.214 ms (24923 allocations: 389.42 KiB)
> \`\`\`
> 
> \> on this PR (37e279bce7136e48f159811641b68143412c3881)
> \`\`\`
> ❯ julia summer.jl
> 421.223 μs (0 allocations: 0 bytes)
> \`\`\`
> 
> ## caveats
>   
> Within the \`Conditional\`/\`InterConditional\` framework, only a single
> constraint can be back-propagated inter-procedurally. This PR implements
> a naive heuristic to "pick up" a constraint to be propagated when a
> return type is a boolean. The heuristic may fail to select an
> "interesting" constraint in some cases. For example, we may expect
> \`::Expr\` constraint to be imposed on the first argument of
> \`Meta.isexpr\`, but the current heuristic ends up picking up a constraint
> on the second argument (i.e. \`ex.head === head\`).
> \`\`\`julia
> isexpr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && ex.head === head
> 
> @test\_broken Base.return\_types((Any,)) do x
> Meta.isexpr(x, :call) && return x # x::Expr, ideally
> return nothing
> end == Any\[Union{Nothing,Expr}\]
> \`\`\`
> 
> I think We can get rid of this limitation by extending \`Conditional\` and
> \`InterConditional\`
> so that they can convey multiple constraints, but I'd like to leave this
> as a future work.
> 
> EDIT: 3df027a implements the "pick up" logic within a caller
> (i.e. within \`abstract\_call\_gf\_by\_type\`), which allows us to choose a
> constraint more appropriately, and now the \`Meta.isexpr\` case is fixed.
> Still there is a limitation of multiple conditional constraint
> back-propagation; the following broken test case will explain the future
> work.
> \`\`\`julia
> is\_int\_and\_int(a, b) = isa(a, Int) && isa(b, Int)
> @test\_broken Base.return\_types((Any,Any)) do a, b
> is\_int\_and\_int(a, b) && return a, b # (a::Int, b::Int) ideally, but (a::Any, b::Int)
> 0, 0
> end == Any\[Tuple{Int,Int}\]
> \`\`\`
> 
> \---
> 
> \- closes #38636
> \- closes #37342

> <https://github.com/JuliaLang/julia/commit/36e7e8a10f4f3d0ddf85ff15bd7439c3927622bf#diff-177aa526ed7d10bcf44dd31c5409c4a937cfab2f336cb1a8055b4c575f95da85>
>
> note that the changes for \`isnothing\` and \`ismissing\` aren't necessary,
> but they… reduce the number of method definitions for good reason;
> the less the number of methods they have, the better we can
> back-propagate type constraints, because even after a package defines
> their own new methods for them we can keep to use our \`InterConditional\`
> logic as far as far as the number of methods is
> \[≤3\](https://github.com/JuliaLang/julia/blob/5c6e21edbfd8f0c7d16ea01c91d1c75c30d4eaa1/base/compiler/types.jl#L119)

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [July 14, 2021, 9:24pm UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/14 "2021-07-14T21:24:39Z")

</div>

```julia
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?.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [July 14, 2021, 9:44pm UTC](https://discourse.julialang.org/t/x-nothing-vs-x-isa-nothing/64546/15 "2021-07-14T21:44:48Z")

</div>

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).
