# Why are unions of concrete types not concrete?

**URL:** <https://discourse.julialang.org/t/why-are-unions-of-concrete-types-not-concrete/95423>\
**Category:** General Usage\
**Tags:** question, type\
**Created:** [March 2, 2023, 5:33am UTC](https://discourse.julialang.org/t/why-are-unions-of-concrete-types-not-concrete/95423 "2023-03-02T05:33:42Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![amansawhney](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/amansawhney/32/45685_2.png) [@amansawhney](https://discourse.julialang.org/u/amansawhney)\
**Post date:** [March 2, 2023, 5:33am UTC](https://discourse.julialang.org/t/why-are-unions-of-concrete-types-not-concrete/95423/1 "2023-03-02T05:33:42Z")

</div>

I am looking to use Julia for a high performance application where I need to iterate through many items. The items can be of various types, but those types are all concrete. I can make a union of those types. It appears to me that Julia is optimizing this data structure and is using C unions to make access fast. Moreover, if I allocate a vector with a union with concreate types using the undef constructor I get a vector with data (which indicates to me that Julia is using C unions and not storing locations to pointers). However, if I check if a vector of unions isbits it returns false. Same for isconcretetype. Why is this the case?

```julia
julia> using InlineStrings

julia> #show that it is allocated when using undef
       println(Vector{Union{Int64, String31}}(undef, 10))
Union{Int64, String31}[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

julia> println(isconcretetype(Union{Int64, String31}))
false

julia> println(isbitstype(Union{Int64, String31}))
false

julia> println(isbits(Vector{Union{Int64, String31}}))
false

```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [March 2, 2023, 6:51am UTC](https://discourse.julialang.org/t/why-are-unions-of-concrete-types-not-concrete/95423/2 "2023-03-02T06:51:30Z")

</div>

> [@amansawhney](#):
>
> ```julia
> julia> println(isconcretetype(Union{Int64, String31}))
> false
> 
> ```

Have you tried this?

```julia
isconcretetype(Vector{Union{Int64, String31}})

```

---

<div class="post-metadata">

**Author:** ![HanD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hand/32/213908_2.png) [@HanD](https://discourse.julialang.org/u/HanD)\
**Post date:** [March 2, 2023, 7:38am UTC](https://discourse.julialang.org/t/why-are-unions-of-concrete-types-not-concrete/95423/3 "2023-03-02T07:38:36Z")

</div>

Hello and welcome!

Don’t worry, your code probably works as expected. The problem is what you assume of your checks shown here. Let’s take it apart:

1. Quoting the manual of `isconcretetype`: _Determine whether type T is a concrete type, meaning it could have direct instances (values x such that typeof(x) === T)._ No specific value (either 12 or `String31("foo")`) will have the type `Union{Int64, String31}`. Either one, yes, but not the union (e.g., `typeof(12) == Int64`). So a union is never a concrete type.
2. For the same reason, it will never be a bitstype. Its instances, however, can be values of bitstypes, just like 12 and `String31("foo")` are bits values:

```julia
julia> isbits(12) && isbits(String31("foo"))
true

```

1. As for the last test, a type (`Vector{Union{Int64, String31}}` is just a type) is never a bits value, no wonder it returns false. The individual items in a vector of such type can and will be bits values:

```julia
julia> v = Vector{Union{Int64, String31}}(undef, 10);
julia> all(isbits, v)
true

```

So overall, you don’t need to worry about the performance of your code when using such a vector. You just need to be more careful with your checks 🙂

HTH.

---

<div class="post-metadata">

**Author:** ![amansawhney](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/amansawhney/32/45685_2.png) [@amansawhney](https://discourse.julialang.org/u/amansawhney)\
**Post date:** [March 2, 2023, 8:05am UTC](https://discourse.julialang.org/t/why-are-unions-of-concrete-types-not-concrete/95423/4 "2023-03-02T08:05:06Z")

</div>

Hey HTH! A follow up to this would be how could I compress such a vector. Previously I was using Blosc to compress large vectors for file storage but now with the Union type I can no longer do that as the union type is not an isbits typel

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [March 2, 2023, 8:16am UTC](https://discourse.julialang.org/t/why-are-unions-of-concrete-types-not-concrete/95423/5 "2023-03-02T08:16:56Z")

</div>

> [@amansawhney](#):
>
> The items can be of various types, but those types are all concrete. I can make a union of those types. It appears to me that Julia is optimizing this data structure and is using C unions to make access fast.

Keep in mind that the performance of working with unions depends on the optimizations that the compiler performs, which can vary a lot depending on the size of the union (and the Julia version).

Working with small unions can be very fast, but AFAIK large unions almost always incur a loss of performance. This is for example the time it takes to sum 1000 floats, displayed as a function of the element type of the vector they are stored in:

```julia
                                  ┌ ┐ 
                          Float64 ┤ 8.39198e⁻⁷                               
               Union{T1, Float64} ┤ 8.89393e⁻⁷                               
           Union{T1, T2, Float64} ┤ 8.9998e⁻⁷                                
       Union{T1, T2, T3, Float64} ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 7.8316e⁻⁵   
   Union{T1, T2, T3, T4, Float64} ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 7.9389e⁻⁵   
                                  └ ┘

```

So it seems that in this instance, working with unions of 3 types or less is optimized. (This is with Julia 1.8.5; I seem to remember that not so long ago only unions of 2 types were optimized).

> **Complete benchmarking code**
>
> x = rand(1000);
> 
> function my\_sum(xs)  
> res = 0.0  
> for x in xs  
> res += x  
> end  
> res  
> end
> 
> struct T1 end  
> struct T2 end  
> struct T3 end  
> struct T4 end  
> struct T5 end
> 
> types = [  
> Float64,  
> Union{Float64, T1},  
> Union{Float64, T1, T2},  
> Union{Float64, T1, T2, T3},  
> Union{Float64, T1, T2, T3, T4},  
> ]
> 
> using BenchmarkTools  
> times = map(types) do t  
> y = t  
> append!(y, x)  
> @belapsed my\_sum($y)  
> end
> 
> using UnicodePlots  
> barplot(string.(types), times)

---

<div class="post-metadata">

**Author:** ![Sevi](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@Sevi](https://discourse.julialang.org/u/Sevi)\
**Post date:** [March 2, 2023, 8:18am UTC](https://discourse.julialang.org/t/why-are-unions-of-concrete-types-not-concrete/95423/6 "2023-03-02T08:18:44Z")

</div>

Regarding the compression:

Maybe you could try JLSO instead

> **[GitHub - invenia/JLSO.jl: Julia Serialized Object (JLSO) file format for...](https://github.com/invenia/JLSO.jl)**
>
> Julia Serialized Object (JLSO) file format for storing checkpoint data. - GitHub - invenia/JLSO.jl: Julia Serialized Object (JLSO) file format for storing checkpoint data.

```julia
julia> v = Vector{Union{Int, String31}}(undef, 1000000);

julia> JLSO.save("large_vector.dat", :v => v);

julia> Base.summarysize(v)
33000040

julia> filesize("large_vector.dat")
13754

julia> JLSO.load("large_vector.dat")[:v] == v
true

# Or keeping it in memory
julia> buf = IOBuffer();

julia> JLSO.save(buf, :v => v);

julia> JLSO.load(seekstart(buf))[:v] == v
true

```

Note that the giant compression ratio in this case is just because the vector with `undef`s contains all zeros 😅

---

<div class="post-metadata">

**Author:** ![amansawhney](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/amansawhney/32/45685_2.png) [@amansawhney](https://discourse.julialang.org/u/amansawhney)\
**Post date:** [March 2, 2023, 8:21am UTC](https://discourse.julialang.org/t/why-are-unions-of-concrete-types-not-concrete/95423/7 "2023-03-02T08:21:20Z")

</div>

Thank you so much for the recommendation! I’ll give it a shot now and will report back.
