Subtyping Conventions for Dicts are Unexpected

If

Int64 <: Real

is true, and I presume Julia follows normal function typing rules (for dicts) where the “return” type can be more specific than indicated by the type annotation, why isn’t the following true?

 Dict(:x=>[1,2], :y=>[1,2]) isa Dict{Symbol, Array{<:Real, 1}}

Do I need to do something like:

Dict(:x=>[1,2], :y=>[1,2]) isa <:Dict{Symbol, Array{Real, 1}}

to ‘ask the question’ properly?

Welcome!

In Julia, type parameters are invariant. So even though Int <: Real it is not true that Array{Int} <: Array{Real}.Your use-case is just another example of such type invariance.

julia> Int <: Real
true

julia> Array{Int} <: Array{Real}
false

You can read more about types here

https://docs.julialang.org/en/v1/manual/types/index.html#Parametric-Types-1

1 Like

What is happening here?

julia> [1,2] isa Array{<:Real,1}
true

and here?

julia> Array{Int} <: Array{<:Real}
true

Your last piece of code is equivalent to checking

julia> Array{Int} <: Array{T} where {T <: Real}
true

The RHS is the set of all types of the form Array{T} where {T<:Real}. So, an element that belongs to Array{Int} is also an element of Array{T} where {T<:Real}. An element that belongs to Array{Float64} is also an element of Array{T} where {T<:Real} and so on

julia> Array{Float64} <: Array{T} where {T <: Real}
true
1 Like

So it seems like I can get the kind of type variance behavior that I intuitively want to use – returning to the original question, why doesn’t this “hack” work for the more complex type? :

julia> Dict{Symbol, Array{Int,1}} <: Dict{Symbol, Array{<:Real, 1}}
false

You need

julia> Dict{Symbol, Array{Int,1}} <: Dict{R, Array{T, 1}} where {R, T}
true

Thanks!
I see that

julia> Dict{Symbol, Array{Int,1}} <: Dict{Symbol, Array{T, 1}} where {T <: Real}
true

also works.

Is the way I am doing this not the “Julian” way to do things?

This is also fine and depends on whether you want to use a fixed type or its subset. What I wrote is slightly more generic because you can replace R with any subtype of Symbol. This is what you are doing with Array{T, 1} where {T<:Real} as well. You are allowing the code to compile for a subtype of Real so you have flexibility in that you can pass a subtype of Real in place of T like so

julia> Dict{Symbol, Array{Int,1}} <: Dict{Symbol, Array{T, 1}} where {T <: Real}
true

julia> Dict{Symbol, Array{Float64,1}} <: Dict{Symbol, Array{T, 1}} where {T <: Real}
true

There is nothing non-Julian in what you did but I like to keep my code as generic as possible :slight_smile: Notice that keeping the code like Array{T, 1} where {T<:Real} has no runtime penalty.

1 Like