# Equality of unwrapped unionalls

**URL:** <https://discourse.julialang.org/t/equality-of-unwrapped-unionalls/128716>\
**Category:** General Usage\
**Created:** [May 5, 2025, 10:33am UTC](https://discourse.julialang.org/t/equality-of-unwrapped-unionalls/128716 "2025-05-05T10:33:43Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![cshen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cshen/32/217287_2.png) [@cshen](https://discourse.julialang.org/u/cshen)\
**Post date:** [May 5, 2025, 10:33am UTC](https://discourse.julialang.org/t/equality-of-unwrapped-unionalls/128716/1 "2025-05-05T10:33:43Z")

</div>

Say you have the following struct

```julia
struct MyArr{T,N}
  arr::Array{T,N}
end

```

I was then wondering why the following equality does not hold:

```julia
unwrap_arr_field = fieldtypes(Base.unwrap_unionall(MyArr))[1]
unwrap_arr_manual = Base.unwrap_unionall(Array{T,N} where {T,N})

@assert (unwrap_arr_field == unwrap_arr_manual) == false
@assert (unwrap_arr_field <: unwrap_arr_manual) == false
@assert (unwrap_arr_manual <: unwrap_arr_field) == false

```

But

```julia
unwrap_arr_field.hash == unwrap_arr_manual.hash

```

Expectedly we have

```julia
@assert fieldtypes(MyArr)[1] == Array{T,N} where {T,N}

```

So there is something happening during the unwrapping of the `UnionAll`.

I’ve the suspicion that this is due to typevars not being equal although being identical

```julia
@assert (TypeVar(:T) == TypeVar(:T)) == false

```

but I don’t really know why this is or if this is indeed the case.

What am I missing in my mental model of julia types? Why does this happen? What happens to the unwrapped unionall type that breaks the equality?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 5, 2025, 11:45am UTC](https://discourse.julialang.org/t/equality-of-unwrapped-unionalls/128716/2 "2025-05-05T11:45:20Z")

</div>

> [@cshen](#):
>
> typevars not being equal although being identical

In Julia and programming languages in general, identity means the “same” instance that cannot be distinguished by any program; this is different than the colloquial sense of similarity. For example, we can distinguish “identical” twins by giving them differently colored hats. For a coding example, twin arrays `[1]` and `[1]` can be distinguished by mutating them to different values. In fact, separate instantiations of a mutable type always result in different instances by definition.

Back to your case, the `T` are not identical (`===`) despite having the same internal values because `TypeVar` is a mutable type, and equality (`==`) falls back to `===` by default:

```julia
julia> unwrap_arr_manual.parameters[1] == unwrap_arr_field.parameters[1]
false

julia> unwrap_arr_manual.parameters[1] === unwrap_arr_field.parameters[1]
false

julia> unwrap_arr_manual.parameters[1], unwrap_arr_field.parameters[1]
(T, T)

julia> typeof.((unwrap_arr_manual.parameters[1], unwrap_arr_field.parameters[1]))
(TypeVar, TypeVar)

julia> ismutabletype(TypeVar)
true

```

Note that all the fields of `TypeVar` are `const` so we can’t reassign them; despite the effective inability to mutate them to different values, they are still distinguishable by memory address, and that is what `===` currently checks.

---

<div class="post-metadata">

**Author:** ![cshen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cshen/32/217287_2.png) [@cshen](https://discourse.julialang.org/u/cshen)\
**Post date:** [May 5, 2025, 2:05pm UTC](https://discourse.julialang.org/t/equality-of-unwrapped-unionalls/128716/3 "2025-05-05T14:05:30Z")

</div>

Ok, thank you for the clarification. I had mistakenly assumed that TypeVars were immutable. The const mutable implementation seems a very explicit way of defining them, which leads me to the followup question of: why do typevars need to be separated even at the instance level even when indicating the same parameter and bounds?

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 5, 2025, 2:51pm UTC](https://discourse.julialang.org/t/equality-of-unwrapped-unionalls/128716/4 "2025-05-05T14:51:03Z")

</div>

It’s easier to flip the question, when do you need the same `TypeVar` instance? When the same type parameter is shared by several places, like `T` in the line `struct MyArr{T,N}` and the line `arr:Array{T,N}` of the definition:

```julia
julia> Base.unwrap_unionall(MyArr).parameters[1] === unwrap_arr_field.parameters[1]
true

```

`TypeVar` identity is how Julia ensures both places get the same `T` value you specify per concrete type. Flipping back to your question, we thus need different instances for different type parameters that happen to share a name and bounds but can be specified with different values. That might have been doable with an immutable type if there was an additional field for identity, but everything has an address, might as well use it.

Source: [More about types · The Julia Language](https://docs.julialang.org/en/v1/devdocs/types/#UnionAll-types)
