# Tuple reverse is not type-stable enough

**URL:** <https://discourse.julialang.org/t/tuple-reverse-is-not-type-stable-enough/43005>\
**Category:** General Usage\
**Created:** [July 13, 2020, 9:21pm UTC](https://discourse.julialang.org/t/tuple-reverse-is-not-type-stable-enough/43005 "2020-07-13T21:21:27Z")\
**Posts on this page:** 5\
**Page:** 1

<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:** [July 13, 2020, 9:21pm UTC](https://discourse.julialang.org/t/tuple-reverse-is-not-type-stable-enough/43005/1 "2020-07-13T21:21:27Z")

</div>

I just start with an MWE:

```julia
# julia 1.4
# ]add AxisKeys
using AxisKeys
using Test

# "more" type-stable version of reverse
reverse_tuple(x) = ntuple(i -> x[length(x) + 1 - i], length(x))

f1(nt) = NamedDimsArray(zeros(1,1,1), keys(nt) |> reverse)
f2(nt) = NamedDimsArray(zeros(1,1,1), keys(nt) |> reverse_tuple)

@inferred f1((a=1,b=1,c=1)) # fails: keys not inferred
@inferred f2((a=1,b=1,c=1)) # passes

```

Basically, function `f1` does not infer correctly, but `f2` does - and the only thing that differs is the tuple reversal function.  
Note that `Base.reverse` **is** type-stable if used by itself, and the example above reproduces only when `reverse` is put inside something else.

Any idea why this happens and what’s going on?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [July 13, 2020, 9:44pm UTC](https://discourse.julialang.org/t/tuple-reverse-is-not-type-stable-enough/43005/2 "2020-07-13T21:44:12Z")

</div>

You’re putting the `(:c, :b, :a)` tuple into a type parameter, so you’re not really fighting type-stability… you’re fighting _constant propagation_. Not only does Julia need to get the type of `reverse` correct, it needs to deduce (and mark) the fact that it’s going to return that particular _value_ ahead of time. That in turn is affecting the downstream type-stability once you put the (hopefully) constant value into a type parameter.

---

<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:** [July 13, 2020, 9:52pm UTC](https://discourse.julialang.org/t/tuple-reverse-is-not-type-stable-enough/43005/3 "2020-07-13T21:52:41Z")

</div>

Note for sufficiently long tuples, `reverse` _will_ become a gigantic runtime performance problem:

```julia
julia> @generated function myreverse(t::NTuple{N, Any}) where {N}
           Expr(:tuple, (:(t[$i]) for i in N:-1:1)...)
       end
myreverse (generic function with 1 method)

julia> for n ∈ 1:5:50
           tt = Ref(Tuple(1:n))
           println("n = $n")
           print(" "); 
           @btime reverse($tt[])
           print(" ");
           @btime myreverse($tt[])
           println(" ")
       end
n = 1
    1.289 ns (0 allocations: 0 bytes)
    1.290 ns (0 allocations: 0 bytes)
 
n = 6
    2.839 ns (0 allocations: 0 bytes)
    2.909 ns (0 allocations: 0 bytes)
 
n = 11
    3.439 ns (0 allocations: 0 bytes)
    3.349 ns (0 allocations: 0 bytes)
 
n = 16
    4.369 ns (0 allocations: 0 bytes)
    4.489 ns (0 allocations: 0 bytes)
 
n = 21
    4.229 ns (0 allocations: 0 bytes)
    4.229 ns (0 allocations: 0 bytes)
 
n = 26
    7.116 ns (0 allocations: 0 bytes)
    6.939 ns (0 allocations: 0 bytes)
 
n = 31
    5.819 ns (0 allocations: 0 bytes)
    5.669 ns (0 allocations: 0 bytes)
 
n = 36
    25.239 μs (72 allocations: 6.47 KiB)
    9.749 ns (0 allocations: 0 bytes)
 
n = 41
    31.559 μs (82 allocations: 8.19 KiB)
    7.657 ns (0 allocations: 0 bytes)
 
n = 46
    38.580 μs (92 allocations: 10.16 KiB)
    12.381 ns (0 allocations: 0 bytes)

```

but this is a separate issue from type stability.

---

<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:** [July 13, 2020, 9:52pm UTC](https://discourse.julialang.org/t/tuple-reverse-is-not-type-stable-enough/43005/4 "2020-07-13T21:52:51Z")

</div>

Indeed, you are right - I didn’t notice that. It means `AxisKeys` interface is not type-stable, and nothing is suboptimal about `Base.reverse` implementation, right?  
Then my `reverse_tuple` is somehow easier for the julia constant propagation, but it has nothing to do with type stability?

---

<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:** [July 13, 2020, 9:59pm UTC](https://discourse.julialang.org/t/tuple-reverse-is-not-type-stable-enough/43005/5 "2020-07-13T21:59:31Z")

</div>

Some `reverse(::Tuple)` performance discussion references:

- [https://github.com/JuliaArrays/StaticArrays.jl/pull/802](https://github.com/JuliaArrays/StaticArrays.jl/pull/802)
- [https://github.com/JuliaLang/julia/pull/16604](https://github.com/JuliaLang/julia/pull/16604)
