# Help with introspection on types

**URL:** https://discourse.julialang.org/t/help-with-introspection-on-types/73899
**Category:** Internals & Design
**Created:** [January 1, 2022, 5:44pm UTC](https://discourse.julialang.org/t/help-with-introspection-on-types/73899 "2022-01-01T17:44:36Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)
#### Post date: [January 1, 2022, 5:44pm UTC](https://discourse.julialang.org/t/help-with-introspection-on-types/73899/1 "2022-01-01T17:44:36Z")

</div>

This question arises from my attempt to write new constructors for DataStructures.jl. Please see the trace below (1.7.1), which raises the following questions:

1. Is there any difference between `<type>.parameters` and `<type>.types`? They gave the same answers in every test below.

2. Why did `collect` return different answers when applied to a tuple versus an array in Tests 3 and 4 and yet there was no difference in Tests 1 and 2?

3. (Main question.) In Test #4, how can I extract the two parameters of the type? I would have expected the two parameters to be `Int` and `Any`. Both `.parameters` and `.types` failed.

```julia
julia> # Test 1

julia> c = collect([(1,3),(2,"a")]);

julia> eltype(c)
Tuple{Int64, Any}

julia> eltype(c).types
svec(Int64, Any)

julia> eltype(c).parameters
svec(Int64, Any)

julia> # Test 2

julia> c = collect(((1,3), (2, "a")));

julia> eltype(c)
Tuple{Int64, Any}

julia> eltype(c).types
svec(Int64, Any)

julia> eltype(c).parameters
svec(Int64, Any)

julia> # Test 3

julia> c = collect([1=>3, 2=>"a"])
2-element Vector{Pair{Int64, Any}}:
 1 => 3
 2 => "a"

julia> eltype(c)
Pair{Int64, Any}

julia> eltype(c).types
svec(Int64, Any)

julia> eltype(c).parameters
svec(Int64, Any)

julia> # Test 4

julia> c = collect((1=>3, 2=>"a"));

julia> eltype(c)
Pair{Int64}

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

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

```

---

<div class="post-metadata">

### Author: ![melonedo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/melonedo/32/15503_2.png) [@melonedo](https://discourse.julialang.org/u/melonedo)
#### Post date: [January 2, 2022, 3:54am UTC](https://discourse.julialang.org/t/help-with-introspection-on-types/73899/2 "2022-01-02T03:54:09Z")

</div>

> [@Stephen\_Vavasis](#):
>
> Why did `collect` return different answers when applied to a tuple versus an array in Tests 3 and 4 and yet there was no difference in Tests 1 and 2?

By tracking down the code, `[1=>3, 2=>"a"]` calls `Base.vect(1=>3, 2=>"a")`, which in turn calls `promote_typeof` (base of `promote`). The type `Vector{Pair{Int,Any}}` is already determined at this point.  
`(1=>3, 2=>"a")` is a tuple type, so the promotion is delayed until `collect`, which calls `promote_typejoin` (base of `typejoin`), which returns `Pair{Int}` (shorthand for `Pair{Int, T} where T`). These are two different types: `Pair{Int,Any}` is a single type, while `Pair{Int}` is a set of types: `Pair{Int, Int}`, `Pair{Int, String}` or even `Pair{Int, Any}`.

The different code paths `promote` vs `typejoin` lead to different result, but I am unclear about the reason though.

> [@Stephen\_Vavasis](#):
>
> (Main question.) In Test #4, how can I extract the two parameters of the type? I would have expected the two parameters to be `Int` and `Any` . Both `.parameters` and `.types` failed.

Since you see their is no single type, you might need to convert them to a single type maybe with some type calculations.

---

<div class="post-metadata">

### Author: ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)
#### Post date: [January 2, 2022, 3:26pm UTC](https://discourse.julialang.org/t/help-with-introspection-on-types/73899/3 "2022-01-02T15:26:03Z")

</div>

Regarding `Pair{Int}`: this appears to be some kind of gap in the Julia type system. As far as I can see, there is no difference between `Vector{Pair{Int,T} where T}` and `Vector{Pair{Int,Any}}`, except that the former is less amenable to type introspection. Note that Julia automatically interprets `Vector{T where T}` as `Vector{Any}`.

Regarding my main problem of inferring the data type of an arbitrary iterable of pairs: below is the constructor I am planning to use. This constructor has the big disadvantage that it copies the data three times. When the type parameters are known, the data is copied just once. (This disadvantage will be documented.) I use the same pattern for `SortedMultiDict`. Better solutions are welcome.

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

```
