Type-instability of Parametrized Structs

First create all the data for our struct attributes.

We are going to create singletons of abstract type Car.

abstract type Car end
struct Volvo <: Car end
struct Mazda <: Car end
0.3s

Then consider the Struct where we want to store our data.

So, lets define a struct as

abstract type GeneralStruct end
struct AA{T} <: GeneralStruct
  vector::Vector{T}
  car::Car
end
0.2s

The basic idea is to test the correct parametrization of complex structs by accessing their attributes.

In order to do that, we define the necessary functions as:

function getvector(mystruct::S) where {S <: GeneralStruct}
  mystruct.vector
end
function getcar(mystruct::S) where  {S <: GeneralStruct}
  mystruct.car
end
1.1s
getcar (generic function with 1 method)

Instantiate our struct with different types

a = AA([1.,2.,3.], Volvo())
0.6s
AA{Float64}([1.0, 2.0, 3.0], Volvo())
b = AA([1,2,3], Mazda())
0.6s
AA{Int64}([1, 2, 3], Mazda())

Each one is parametrized according to its input data. So, lets check for type stability.

@code_warntype getvector(a)
3.7s
@code_warntype getcar(a)
0.4s

So there seems to be an issue with how we parametrized the car attributes.

Therefore, we should better work with a new structure that also parametrizes the singletons of type Car as follows:

struct AB{T, C <: Car} <: GeneralStruct
  vector::Vector{T}
  car::C
end
0.2s
a = AB([1., 2., 3.], Volvo())
0.4s
AB{Float64,Volvo}([1.0, 2.0, 3.0], Volvo())

This new object has parametrized all the data objects inside {Float64, Volvo} , so the compiler nows in advance what kind of objects to expect.

@code_warntype getcar(a)
0.4s

Now everything seems to be working fine.

The next step is try to make a collection with these structs, i.e., a dictionary or tuples. Let's see the difference.

aa = Dict{Symbol,T where T <: GeneralStruct}(:a=> a, :b=> b)
1.1s
Dict{Symbol,GeneralStruct} with 2 entries: :a => AB{Float64,Volvo}([1.0, 2.0, 3.0], Volvo()) :b => AA{Int64}([1, 2, 3], Mazda())
bb = (; :a => a, :b => b)
0.6s
(a = AB{Float64,Volvo}([1.0, 2.0, 3.0], Volvo()), b = AA{Int64}([1, 2, 3], Mazda()))

Dictionaries in this case doesn't seem to parametrize correctly these objects whereas for the NamedTuple created this problem is not present. Let's check this with an example.

First define a function that retrieves an element from it of type GeneralStruct.

function getfroma(a::Dict)
    a[:b]
end
function getfroma(a::NamedTuple)
    a[end]
end
0.5s
getfroma (generic function with 2 methods)
@code_warntype getfroma(aa)
0.4s
@code_warntype getfroma(bb)
0.4s

Clearly the dictionary doesn't parametrize correctly the object inside, whereas for the named tuple it works correclty.

Shift+Enter to run
Runtimes (1)