# Parametric and Union Types

**URL:** <https://discourse.julialang.org/t/parametric-and-union-types/34932>\
**Category:** General Usage\
**Tags:** parametric-types\
**Created:** [February 20, 2020, 6:32pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932 "2020-02-20T18:32:52Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![fedario](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fedario](https://discourse.julialang.org/u/fedario)\
**Post date:** [February 20, 2020, 6:32pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/1 "2020-02-20T18:32:52Z")

</div>

Hi Community!

While playing around with types, some questions came up:

1. 

What is the difference between `Type{<:Union{Int,String}}` and `Union{Type{Int}, Type{String}}`?

```nohighlight
Type{<:Union{Int,String}} |> supertype # Any
Type{<:Union{Int,String}} |> eltype # Any
Int isa Type{<:Union{Int,String}} # true
Union{Type{Int}, Type{String}} |> supertype # ERROR: MethodError
Union{Type{Int}, Type{String}} |> eltype # Any
Int isa Union{Type{Int}, Type{String}} # true
Type{<:Union{Int,String}} == Union{Type{Int}, Type{String}} # false

```

1. 

Are parametric types without their parameters really types? Are they higher kinded types?  
Why is `Array{Int} <: Array` true but, `Array` is nowhere to find when iteratively checking `supertype`s of `Array{Int}`?

```nohighlight
Array{Int} <: Array # true
Array{Int} |> supertype # DenseArray{Int64,N} where N
Array{Int} |> supertype |> supertype # AbstractArray{Int64,N} where N
Array{Int} |> supertype |> supertype |> supertype # Any

```

Edit:

1. 

Is there a type that represents an `Array` that can only hold `Int` and `String` typed elements?

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [February 21, 2020, 4:23am UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/2 "2020-02-21T04:23:40Z")

</div>

I’m going to attempt to rescue this thread before it gets completely buried. I might not be able to answer all your questions.

## 2

> [@fedario](#):
>
> Are parametric types without their parameters really types? Are they higher kinded types?  
> Why is `Array{Int} <: Array` true but, `Array` is nowhere to find when iteratively checking `supertype` s of `Array{Int}` ?

Parametric types with their parameters unspecified are abstract types. In particular, they are referred to as [UnionAll Types](https://docs.julialang.org/en/v1/manual/types/#UnionAll-Types-1). They represent the union of all types that are possible by assigning a concrete type to each type parameter.

`Array` is shorthand for `Array{T,N} where N where T`. Note that you can partially instantiate a parametric type:

```julia
julia> Array{Int64} == Array{Int64,N} where N
true

```

A partially instantiated parametric type is still an abstract type:

```julia
julia> isconcretetype(Array)
false

julia> isconcretetype(Array{Float64})
false

julia> isconcretetype(Array{Float64,1})
true

```

## 3

> [@fedario](#):
>
> Is there a type that represents an `Array` that can only hold `Int` and `String` typed elements?

Yes, if we restrict ourselves to one-dimensional arrays, then we have the following concrete type:

```julia
Array{Union{Int,String},1}

```

If the number of dimensions is left unspecified, then we have an abstract type:

```julia
julia> IntStringArray{N} = Array{Union{Int, String}, N} where N
Array{Union{Int64, String},N} where N

julia> isconcretetype(IntStringArray)
false

julia> isconcretetype(IntStringArray{2})
true

julia> x = IntStringArray(undef, 0)
0-element Array{Union{Int64, String},1}

julia> push!(x, 4)
1-element Array{Union{Int64, String},1}:
 4

julia> push!(x, "hello")
2-element Array{Union{Int64, String},1}:
 4       
  "hello"

julia> push!(x, 1.2)
ERROR: MethodError: Cannot `convert` an object of type Float64 to an object of type Union{Int64, String}

```

## 1

> [@fedario](#):
>
> What is the difference between `Type{<:Union{Int,String}}` and `Union{Type{Int}, Type{String}}` ?

I’m not sure if this is a point of confusion or not, but a type such as `Type{Int64}` is a sort of meta-type that represents the type object `Int64` rather than an instance of `Int64`. For example, we can make two separate `foo` methods as follows:

```julia
julia> foo(::Int64) = 1
foo (generic function with 1 method)

julia> foo(::Type{Int64}) = 2
foo (generic function with 2 methods)

julia> methods(foo)
# 2 methods for generic function "foo":
[1] foo(::Int64) in Main at REPL[1]:1
[2] foo(::Type{Int64}) in Main at REPL[2]:1

julia> foo(100)
1

julia> foo(Int64)
2

```

Regarding the difference between

`Type{T} where T<:Union{Int, String}`

and

`Union{Type{Int}, Type{String}}`

…They do seem to be functionally equivalent, but Julia doesn’t seem to be able to do the algebra/set operations needed to declare them `==`. I’m not sure precisely what’s going on there.

**Edit:**

It could be because the first is a `UnionAll` type and the second is a `Union` type:

```julia
julia> typeof(Type{T} where T<:Union{Int, String})
UnionAll

julia> typeof(Union{Type{Int}, Type{String}})
Union

```

It looks like Julia doesn’t have a specialized `==` method for comparing `Union` and `UnionAll` types:

```julia
julia> (Type{T} where T<:Union{Int, String}) == Union{Type{Int}, Type{String}}
false

julia> @which (Type{T} where T<:Union{Int, String}) == Union{Type{Int}, Type{String}}
==(T::Type, S::Type) in Base at operators.jl:162

```

It might be possible to implement a specialized `==(::Union, ::UnionAll)` method that would catch this sort of scenario.

---

<div class="post-metadata">

**Author:** ![fedario](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fedario](https://discourse.julialang.org/u/fedario)\
**Post date:** [February 21, 2020, 9:43am UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/3 "2020-02-21T09:43:06Z")

</div>

Thank you, that really helps!

Regarding **2.** : So UnionAll types are unions which include all types defined by parametric types, but are not their supertype, correct?

I also wonder why Union and UnionAll are separate types. Maybe because the latter are infinite sets?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [February 21, 2020, 9:50am UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/4 "2020-02-21T09:50:57Z")

</div>

They are different beasts, `UnionAll` is for unions over parameters (`... where ...`), eg

```julia
julia> Vector
Array{T,1} where T

julia> typeof(Vector)
UnionAll

```

while `Union` is for enumerated types.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [February 21, 2020, 3:58pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/5 "2020-02-21T15:58:55Z")

</div>

> [@fedario](#):
>
> So UnionAll types are unions which include all types defined by parametric types, but are not their supertype, correct?

I’m not sure if I understand your exact meaning here, but to clarify:

`Union` and `UnionAll` are types of types. (The other common type of type is [`DataType`](https://docs.julialang.org/en/v1/manual/types/#Declared-Types-1).) So, `Array{T,1} where T` is a type of type `UnionAll`, but arrays of type `Array{T,1} where T` are _not_ a subtype of `UnionAll`:

```julia
julia> typeof(Array{T,1} where T)
UnionAll

julia> (Array{T,1} where T) <: UnionAll
false

```

And similarly for `Union` types:

```julia
julia> typeof(Union{Int64, String})
Union

julia> Union{Int64, String} <: Union
false

```

In other words, `Array{T,N} where N where T` is a node on the hierarchy of types of arrays, whereas `UnionAll` is a node on the hierarchy of types of types. For example,

```julia
julia> Array{Int64,1} <: (Array{T,N} where N where T)
true

julia> (Array{T,N} where N where T) <: AbstractArray
true

julia> isconcretetype(UnionAll)
true

julia> supertype(UnionAll)
Type{T}

julia> supertype(supertype(UnionAll))
Any

```

---

<div class="post-metadata">

**Author:** ![fedario](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fedario](https://discourse.julialang.org/u/fedario)\
**Post date:** [February 21, 2020, 6:05pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/6 "2020-02-21T18:05:28Z")

</div>

What still confuses me is the snipped under my original **2** :

```nohighlight
Array{Int} <: Array # true
Array{Int} |> supertype # DenseArray{Int64,N} where N
Array{Int} |> supertype |> supertype # AbstractArray{Int64,N} where N
Array{Int} |> supertype |> supertype |> supertype # Any

```

Why is it that `Array{Int}` is a subtype of `Array` (according to `<:`) but `Array` is not a supertype of `Array{Int}`? I was thinking one implied the other.

In fact, `Array` does not even have subtypes:

```nohighlight
Array |> subtypes # 0-element Array{Type,1}

```

This made me think that a parametric type without its parameters specified is not a type. At least not of the same kind as other types.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [February 21, 2020, 7:38pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/7 "2020-02-21T19:38:42Z")

</div>

> [@fedario](#):
>
> Why is it that `Array{Int}` is a subtype of `Array` (according to `<:` ) but `Array` is not a supertype of `Array{Int}` ? I was thinking one implied the other.

It is my understanding that `supertype()` and `subtypes()` only return the explicitly declared supertype and subtypes, respectively. `Array{Int,1}` and `Array{String,1}` are not explicitly declared types. They are only declared implicitly through the parametric definition `Array{T,N} where {T,N}`. (Henceforth, I will drop the `where {T,N}` for brevity.) Therefore, `subtypes(Array)` returns an empty array because there are no explicitly declared subtypes of `Array{T,N}`.

The particular branch of the type hierarchy (tree) that we’re looking at is explicitly declared as the following:

```julia
AbstractArray ⟶ DenseArray ⟶ Array

```

Or with the type parameters made explicit:

```julia
AbstractArray{T,N} where {T,N}
    ⮑ DenseArray{T,N} where {T,N}
           ⮑ Array{T,N} where {T,N}

```

Note that `Array{T,N}` is a terminal node in the tree. There are no explicitly declared subtypes of `Array{T,N}`.

So, you could say that `Array{Int,1}` lives on the same node in the hierarchy as `Array{T,N}`. But `Array{Int,1}` is a subtype of `Array{T,N}` because it represents a subset of the various types that are implied by `Array{T,N}`.

Said another way, `Array{T,N}` is a supertype of `Array{Int,1}`, but it’s not returned by `supertype(Array{Int,1})` because it lives on the same node as `Array{Int,1}`.

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [February 21, 2020, 7:53pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/8 "2020-02-21T19:53:07Z")

</div>

`Array{Int} <: Array && Array >: Array{Int}`, those are symmetric operations.

---

<div class="post-metadata">

**Author:** ![fedario](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fedario](https://discourse.julialang.org/u/fedario)\
**Post date:** [February 21, 2020, 7:53pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/9 "2020-02-21T19:53:31Z")

</div>

Yea, I guess it works like that.  
It does feel a bit unsatisfying on a formal level, though. 😄

Thank you for your answers!  
Oh and did you create those type hierarchy “visualizations” yourself or is there some function to print the type graph?

---

<div class="post-metadata">

**Author:** ![fedario](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fedario](https://discourse.julialang.org/u/fedario)\
**Post date:** [February 21, 2020, 7:57pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/10 "2020-02-21T19:57:15Z")

</div>

I get that, but I think it’s strange (or unsatisfying) that if `A <: B` is `true`, `subtypes(B)` does not list `A`.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [February 21, 2020, 8:05pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/11 "2020-02-21T20:05:02Z")

</div>

> [@fedario](#):
>
> Oh and did you create those type hierarchy “visualizations” yourself or is there some function to print the type graph?

I wrote them out by hand. I thought I saw a package once for making Julia type graphs, but I can’t find it now.

> [@fedario](#):
>
> I think it’s strange (or unsatisfying) that if `A <: B` is `true` , `subtypes(B)` does not list `A` .

`Array{T,N}` has an infinite number of subtypes, so I think it’s reasonable to not list them all out. 🙂

---

<div class="post-metadata">

**Author:** ![fedario](https://avatars.discourse-cdn.com/v4/letter/f/f14d63/32.png) [@fedario](https://discourse.julialang.org/u/fedario)\
**Post date:** [February 21, 2020, 8:11pm UTC](https://discourse.julialang.org/t/parametric-and-union-types/34932/12 "2020-02-21T20:11:50Z")

</div>

Fair enough. 😄
