# Incomplete description of type vector of pairs

Why does Julia not fully describe the type of the elements of the pair vector?

shouldn’t it be the union of the types of the individual elements of the vector?

``````Pair{Tuple{Char, Int64, Int64}, Float64}
#and
Pair{Tuple{Char, Int64, Int64}, Nothing}
``````
``````18-element Vector{Pair{Tuple{Char, Int64, Int64}}}:
('p', 1, 2) => 2.5
('q', 1, 2) => 2.5
('p', 1, 7) => 4.1
('q', 1, 7) => nothing
('p', 2, 3) => 7.4
('q', 2, 3) => 7.4
('p', 2, 5) => nothing
('q', 2, 5) => 5.6
('p', 3, 4) => nothing
('q', 3, 4) => nothing
('p', 3, 5) => nothing
('q', 3, 5) => nothing
('p', 4, 1) => nothing
('q', 4, 1) => nothing
('p', 4, 5) => nothing
('q', 4, 5) => nothing
('p', 5, 2) => nothing
('q', 5, 2) => 3.2

julia> typeof([ (person,pair...)=>person_values(person,pair...)  for (person,pair) in
Iterators.product(Persons,Pairs)][:])
Vector{Pair{Tuple{Char, Int64, Int64}}} (alias for Array{Pair{Tuple{Char, Int64, Int64}}, 1})

julia> typeof([ (person,pair...)=>person_values(person,pair...)  for (person,pair) in
Iterators.product(Persons,Pairs)][:][1])
Pair{Tuple{Char, Int64, Int64}, Float64}

julia> typeof([ (person,pair...)=>person_values(person,pair...)  for (person,pair) in
Iterators.product(Persons,Pairs)][:][4])
Pair{Tuple{Char, Int64, Int64}, Nothing}
``````

I am also hoping somebody knowledgeable will answer this question because I have a similar issue. In the proposed revision (https://github.com/JuliaCollections/DataStructures.jl/pull/787) of sorted containers from DataStructures.jl, I rewrote constructors to fully infer types when given untyped collections of pairs, e.g., `SortedDict(Base.Forward, (1=>"a", 2=>7))`. Here is the relevant code:

``````function SortedDict(o::Ordering, kv)
c = collect(kv)
if eltype(c) <: Pair
c2 = collect((t.first, t.second) for t in c)
elseif eltype(c) <: Tuple
c2 = collect((t[1], t[2]) for t in c)
else
throw(ArgumentError("In SortedDict(o,kv), kv should contain either pairs or 2-tuples"))
end
SortedDict{eltype(c2).parameters[1], eltype(c2).parameters[2], typeof(o)}(o, c2)
end
``````

Notice that this constructor copies the data three times in a row in order to infer the types. The reason that three copies are needed is the following Julia behavior:

``````julia> typeof(collect((1=>"a",2=>7)))
Vector{Pair{Int64}} (alias for Array{Pair{Int64}, 1})
``````

I could remove at least one of the three copying operations if the above statement returned `Vector{Pair{Int64,Any}}` or `Vector{Pair{Int64, Union{String,Int64}}` instead of the inner type `Pair{Int64}` that has a free parameter.

Note that neither of those will actually work because julia types are invariant:

``````julia> Pair{Int, String} <: Pair{Int, Any}
false

julia> Pair{Int, String} <: Pair{Int, Union{String, Int}}
false
``````

so `collect` won’t return a collection with either of those element types, since a vector of that type can’t hold any of the things you’ve put into it.

The syntax `Pair{Int}` is just a shorthand for the set of all `Pair` types with `Int` as their first parameter:

``````julia> Pair{Int} == Pair{Int, T} where T
true
``````

(incidentally, I’m surprised that `===` returns false in the above case).

That’s not to say, though, that a `Vector{Pair{Int, Union{Int, String}}}` is an unreasonable thing to want. But constructing one will involve making a new `Pair{Int, Union{...}}` for each element, which is something `collect` isn’t going to do.

I agree that `Pair{Int,Int}` is not a subtype of `Pair{Int,Any}`, but I disagree that `Vector{Pair{Int,Any}}` is unsuitable for the requested operation:

``````julia> u = Pair{Int,Any}[1=>"a",2=>7]
2-element Vector{Pair{Int64, Any}}:
1 => "a"
2 => 7

julia> typeof(u)
Vector{Pair{Int64, Any}} (alias for Array{Pair{Int64, Any}, 1})

julia> push!(u, 3=>9.9)
3-element Vector{Pair{Int64, Any}}:
1 => "a"
2 => 7
3 => 9.9
``````

So I still think it would be reasonable (and helpful in my use-case) for `collect` to return `Vector{Pair{Int,Any}}`.

not sure I have fully understood the argument on the functioning of collect in relation to the determination of the types of the elements making up the vector.
I try to submit two other examples that if clarified I can allow myself to better understand how things are going.

functions derive this discussion

Here the compréhension completely determines the type of the components the dict

``````julia> [ (p,pp...)=>pv(p,pp) for(p,pp) in Iterators.flatten([k.=>v for (k,v) in d1])]
7-element Vector{Pair{Tuple{Char, Int64, Int64}, Float64}}:
('p', 1, 2) => 2.5
('p', 1, 7) => 4.1
('p', 2, 3) => 7.4
('q', 1, 2) => 2.5
('q', 2, 3) => 7.4
('q', 2, 5) => 5.6
('q', 5, 2) => 3.2
``````

here not

``````ulia> [ (person,pair...)=>pv(person,pair)  for (person,pair) in
Iterators.product(Persons,Pairs)][:]
18-element Vector{Pair{Tuple{Char, Int64, Int64}}}:
('p', 1, 2) => 2.5
('q', 1, 2) => 2.5
('p', 1, 7) => 4.1
('q', 1, 7) => nothing
('p', 2, 3) => 7.4
('q', 2, 3) => 7.4
('p', 2, 5) => nothing
('q', 2, 5) => 5.6
('p', 3, 4) => nothing
('q', 3, 4) => nothing
('p', 3, 5) => nothing
('q', 3, 5) => nothing
('p', 4, 1) => nothing
('q', 4, 1) => nothing
('p', 4, 5) => nothing
('q', 4, 5) => nothing
('p', 5, 2) => nothing
('q', 5, 2) => 3.2
``````

but if I define the dictionary by hand, I get

``````julia> Dict(
('p', 1, 2) => 2.5,
('q', 1, 2) => 2.5,
('p', 1, 7) => 4.1,
('q', 1, 7) => nothing,
('p', 2, 3) => 7.4,
)
Dict{Tuple{Char, Int64, Int64}, Union{Nothing, Float64}} with 5 entries:
('p', 1, 7) => 4.1
('q', 1, 7) => nothing
('p', 2, 3) => 7.4
('p', 1, 2) => 2.5
('q', 1, 2) => 2.5
``````
``````julia> [('p', 1, 2) => 2.5
('q', 1, 2) => 2.5
('p', 1, 7) => 4.1
('q', 1, 7) => nothing]
4-element Vector{Pair{Tuple{Char, Int64, Int64}, Union{Nothing, Float64}}}:
('p', 1, 2) => 2.5
('q', 1, 2) => 2.5
('p', 1, 7) => 4.1
('q', 1, 7) => nothing
``````

Let me just interject: for my own use-case, it would be OK that `collect((1=>"a",2=>7)` returns an object of type `Vector{Pair{Int}}` rather than `Vector{Pair{Int,Any}}` if only I knew of a documented (i.e., likely to persist in future versions of Julia) method to retrieve the types of the inner objects. This snippet illustrates the issue:

``````julia> u = Pair{Int,Any}[1=>"a",2=>7]
2-element Vector{Pair{Int64, Any}}:
1 => "a"
2 => 7

julia> eltype(u).parameters[1]
Int64

julia> u=collect((1=>"a",2=>7))
2-element Vector{Pair{Int64}}:
1 => "a"
2 => 7

julia> eltype(u).parameters[1]
ERROR: type UnionAll has no field parameters
Stacktrace:
[1] getproperty(x::Type, f::Symbol)
@ Base .\Base.jl:37
[2] top-level scope
@ REPL[39]:1
``````