# How to get a tuple's field using a runtime index in a type stable way?

**URL:** <https://discourse.julialang.org/t/how-to-get-a-tuples-field-using-a-runtime-index-in-a-type-stable-way/111629>\
**Category:** General Usage\
**Created:** [March 14, 2024, 7:17pm UTC](https://discourse.julialang.org/t/how-to-get-a-tuples-field-using-a-runtime-index-in-a-type-stable-way/111629 "2024-03-14T19:17:46Z")\
**Posts on this page:** 5\
**Page:** 2

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [March 15, 2024, 3:07pm UTC](https://discourse.julialang.org/t/how-to-get-a-tuples-field-using-a-runtime-index-in-a-type-stable-way/111629/21 "2024-03-15T15:07:03Z")

</div>

Doesn’t seem very relevant to the problem at hand though:

```julia-repl
julia> using UnionCollections

julia> tup = (1, 2, 3.0, 4.0, 5.0);

julia> unioncollection(tup)
ERROR: MethodError: no method matching unioncollection(::Tuple{Int64, Int64, Float64, Float64, Float64})

Closest candidates are:
  unioncollection(::AbstractArray)
   @ UnionCollections ~/.julia/packages/UnionCollections/bH09Q/src/UnionCollections.jl:19

```

Though, in fairness, TypeSortedCollections.jl seems to just handle the `Tuple` case by collecting it and treating it as a vector, which is… IMO not good, but I guess it depends on @Larbino1’s use-case if that’s acceptable or not.

---

<div class="post-metadata">

**Author:** ![Larbino1](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/larbino1/32/37831_2.png) [@Larbino1](https://discourse.julialang.org/u/Larbino1)\
**Post date:** [March 15, 2024, 3:58pm UTC](https://discourse.julialang.org/t/how-to-get-a-tuples-field-using-a-runtime-index-in-a-type-stable-way/111629/22 "2024-03-15T15:58:53Z")

</div>

If I may, I think you’re missing the point in my question, which is understandable as it is not a typical way to use tuples. If the compiler knows the index at compile time, it knows the type. I am talking about a situation where the compiler does not know the index at compile time.

I think that `TypeSortedCollections` is a much more idiomatic, sensible, way to structure my data and to solve this problem, so will pursue that moving forward, but for now I’m using @raminammour 's nice solution!

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [March 16, 2024, 1:49am UTC](https://discourse.julialang.org/t/how-to-get-a-tuples-field-using-a-runtime-index-in-a-type-stable-way/111629/23 "2024-03-16T01:49:46Z")

</div>

> [@Mason](#):
>
> Doesn’t seem very relevant to the problem at hand though:

Well, tuples are already as type-stable as one can get in Julia (:  
I was suggesting UnionCollections.jl for this situation:

> [@Larbino1](#):
>
> This way, we don’t create huge tuples (only as big as the numbe of types), we can still map/foreach etc. over the collection;  
> `map(press, button_collection)`  
> by implementing map on our collection, and we can still go back inside the collection and get the items in a type stable way

---

<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:** [March 16, 2024, 11:02am UTC](https://discourse.julialang.org/t/how-to-get-a-tuples-field-using-a-runtime-index-in-a-type-stable-way/111629/24 "2024-03-16T11:02:18Z")

</div>

> [@Larbino1](#):
>
> I think that `TypeSortedCollections` is a much more idiomatic, sensible, way to structure my data and to solve this problem, so will pursue that moving forward, but for now I’m using @raminammour 's nice solution!

The solution is sensitive to the tuple input. Right-hand type assertions introduce `typeassert`s that the compiler can leverage for type inference, but they isolate rather than outright eliminate runtime type checks, which includes sugarcoating `@code_warntype` reports. Something I neglected to notice earlier is that the allocation count being interpreted as a sign of instability is also affected by `@time` being in the global scope or whether you’re retrieving one of the interned `Int`s:

```julia
julia> let # 2nd run, omitted 1st run; no allocation like in global scope
         tup = (10, 100im, 1., 2.0im, 3.)
         i = 2
         @time test4(tup, i, Complex{Int})
       end
  0.000001 seconds
0 + 100im

julia> let # 2nd run, omitted 1st run; 1 allocation shows up with non-literal tuple
         tup = Tuple(append!(Any[10, 100im, 1., 2.0im, 3.], fill(1im, 0) ) )
         i = 2
         @time test4(tup, i, Complex{Int})
       end
  0.000008 seconds (1 allocation: 32 bytes)
0 + 1im

julia> let # 2nd run, omitted 1st run; 33+ elements scale in allocations
         tup = Tuple(append!(Any[10, 100im, 1., 2.0im, 3.], fill(1im, 33) ) )
         i = 20
         @time test4(tup, i, Complex{Int})
       end
  0.000026 seconds (221 allocations: 11.000 KiB)
0 + 1im

julia> let # 2nd run, omitted 1st run, retrieving interned Int saves an allocation
         tup = Tuple(append!(Any[10, 100im, 1., 2.0im, 3.], fill(1im, 20) ) )
         i = 1
         @time test4(tup, i, Int)
       end
  0.000007 seconds
10

```

As someone mentioned earlier, omitting the type assertion (`x::T` to `x`) does not change the performance, even though the `@code_warntype` switches from “type-stable” to returning `::Any`. Limiting the number of types to help `@code_warntype` infer better doesn’t affect allocations, either. This makes me think that the allocations in this case actually has little to do with type instability.

I had assumed that `getindex` would show the same effects, but it actually scales better (I don’t know why, might be a perk of calling a built-in function to do the work):

```julia
julia> let
         tup = (10, 100im, 1., 2.0im, 3.)
         i = 2
         @time getindex(tup, i)
       end
  0.000005 seconds
0 + 100im

julia> let
         tup = Tuple(append!(Any[10, 100im, 1., 2.0im, 3.], fill(1im, 0) ) )
         i = 2
         @time getindex(tup, i)
       end
  0.000001 seconds (1 allocation: 32 bytes)
0 + 1im

julia> let
         tup = Tuple(append!(Any[10, 100im, 1., 2.0im, 3.], fill(1im, 33) ) )
         i = 20
         @time getindex(tup, i)
       end
  0.000001 seconds (1 allocation: 32 bytes)
0 + 1im

```

---

<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 16, 2024, 9:59pm UTC](https://discourse.julialang.org/t/how-to-get-a-tuples-field-using-a-runtime-index-in-a-type-stable-way/111629/25 "2024-03-16T21:59:14Z")

</div>

Dunno why this seems to help, but this seems to help:

```julia
julia> _tup = (10, 100, 1., 2., 3.)
       tup::typeof(_tup) = _tup

       function test(tup, idx, type::Type{T}) where T
           ret::T = tup[idx]
           ret
       end

       @time test(tup, 1, Int)
       @time test(tup, 1, Int)
  0.000001 seconds
  0.000001 seconds
10

```

[Previous page](https://discourse.julialang.org/t/how-to-get-a-tuples-field-using-a-runtime-index-in-a-type-stable-way/111629.md?page=1)
