Using `<:`(subtype evaluate-operator) in the Conditional Evaluation (if-else)

MWE:

julia> a = [1,2]
2-element Array{Int64,1}:
 1
 2

julia> typeof(a) <: Array{<:AbstractArray, 1}
false

julia> if typeof(a) <: Array{<:AbstractArray, 1} == false
       println("False!")
       end

julia> if typeof(a) <: Array{<:AbstractArray, 1}
       println("False!")
       end

julia> if (typeof(a) <: Array{<:AbstractArray, 1})
       println("False!")
       end

julia> if (typeof(a) <: Array{<:AbstractArray, 1}) ==  false
       println("False!")
       end
False!

Is this an intentional design? Why do I have to put a pair of parentheses outside typeof(a) <: Array{<:AbstractArray, 1} instead of just using <: like other binary operators such as <?
Thank you!

Ok, this took me a bit to figure out.

TL;DR: chaining comparisons use short-circuiting thus the comparison in OP does not reach == false. More readable is to use !:

!(typeof(a) <: Array{<:AbstractArray, 1})

Long story:

In hind-sight it shouldn’t have taken so long to figure out. Here how I went about it:

Further reduced:

julia> Int <: Float64 == false
false

julia> (Int <: Float64) == false
true

julia> Int <: (Float64 == false)
ERROR: TypeError: in <:, expected Type, got a value of type Bool
Stacktrace:
 [1] top-level scope at REPL[15]:1

Looking at the lowered code for the top example:

julia> Meta.lower(Main, :(Int <: Float64 == false))
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─ %1 = Int <: Float64
└──      goto #3 if not %1
2 ─ %3 = Float64 == false
└──      return %3
3 ─      return false
))))

which can be tidied up to give:

if Int <: Float64
    false
else
    Float64 == false
end

And that finally made it click for me: chaining comparison use short-circuiting (see Mathematical Operations and Elementary Functions · The Julia Language). Thus

Int <: Float64 == false # -> false

is not equivalent to

(Int <: Float64) == false

but to

Int <: Float64 && Float64 == false # (edited)

and as the first term is false, the second never gets evaluated.

Or put into another example:

julia> 5 < 4 > "haha"
false

julia> 2 < 4 > "haha"
ERROR: MethodError: no method matching isless(::String, ::Int64)
6 Likes

Shouldn’t it be:…?

Int <: Float64 && Float64 == false
3 Likes

Yes, thanks. Edited above.

Thanks! this helps clarify a lot!

1 Like

For type checking, consider using the isa operator:

if !(a isa Array{<:AbstractArray, 1})
    println("False!")
end
5 Likes