# Type stability / tuple lengths

**URL:** <https://discourse.julialang.org/t/type-stability-tuple-lengths/63867>\
**Category:** General Usage\
**Tags:** type-stability\
**Created:** [July 1, 2021, 2:05am UTC](https://discourse.julialang.org/t/type-stability-tuple-lengths/63867 "2021-07-01T02:05:48Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![nchisholm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nchisholm/32/37414_2.png) [@nchisholm](https://discourse.julialang.org/u/nchisholm)\
**Post date:** [July 1, 2021, 2:05am UTC](https://discourse.julialang.org/t/type-stability-tuple-lengths/63867/1 "2021-07-01T02:05:48Z")

</div>

Consider the following function that, given a tuple `t`, computes partial products of the elements specified by a second tuple `parts` as follows

```julia
function partialprods(t::NTuple{N,Int}, parts::NTuple{M,Int}) where {N,M}
    all(0 ≤ i ≤ length(t) for i ∈ parts) || error("Dimension out of range.")
    issorted(parts) || error("`parts` must be sorted in ascending order")
    ntuple(i -> prod(t[parts[i]+1 : parts[i+1]]), Val(M-1))
end

julia> partialprods((1,2,3,4), (0,2,4))
(2, 12)

```

That is, adjacent elements of `parts` specify ranges over which to compute the product, and `partialprods` outputs a tuple containing them.

I observed the following interesting behavior where `partialprods` is only sometimes type stable (in Julia 1.6.0)

```julia
julia> wrapper1() = partialprods((1,2,3,4), (0,2,4))
wrapper1 (generic function with 1 method)

julia> wrapper1()
(2, 12)

julia> @code_warntype wrapper1()
Variables
  #self#::Core.Const(wrapper1)

Body::Tuple{Int64, Int64}
1 ─ %1 = Core.tuple(1, 2, 3, 4)::Core.Const((1, 2, 3, 4))
│ %2 = Core.tuple(0, 2, 4)::Core.Const((0, 2, 4))
│ %3 = Main.partialprods(%1, %2)::Core.Const((2, 12))
└── return %3

julia> wrapper2() = partialprods((1,2,3,4), (1,3,4))
wrapper2 (generic function with 1 method)

julia> wrapper2()
(6, 4)

julia> @code_warntype wrapper2()
Variables
  #self#::Core.Const(wrapper2)

Body::Tuple{Any, Any}
1 ─ %1 = Core.tuple(1, 2, 3, 4)::Core.Const((1, 2, 3, 4))
│ %2 = Core.tuple(1, 3, 4)::Core.Const((1, 3, 4))
│ %3 = Main.partialprods(%1, %2)::Tuple{Any, Any}
└── return %3

```

I would be grateful to anyone who could explain to me what I’ve done that introduces the type instability here. It seems the output type of `partialprods` should be inferrable from the length of `parts`, but it is not (always). Note that the first case seems special in that I could not identify another that gave type stable behavior.

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [July 1, 2021, 5:57am UTC](https://discourse.julialang.org/t/type-stability-tuple-lengths/63867/2 "2021-07-01T05:57:41Z")

</div>

`t[parts[i]+1 : parts[i+1]]` is not type stable, unless the compiler can know ahead of time that this is of constant length as a function of `i`, like in your type stable example.  
Instead of slicing to make tuples of dynamic lengths, use a loop or `prod` with a function argument to index into `t`.

---

<div class="post-metadata">

**Author:** ![nchisholm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nchisholm/32/37414_2.png) [@nchisholm](https://discourse.julialang.org/u/nchisholm)\
**Post date:** [July 1, 2021, 2:46pm UTC](https://discourse.julialang.org/t/type-stability-tuple-lengths/63867/3 "2021-07-01T14:46:41Z")

</div>

Thank you - I see now. Looks like one should generally avoid indexing tuples with non-constant ranges. In my original function, I thought the `prod` might serve as a barrier since it results, I think, in an `Int` for any `NTuple{N,Int} where N` (without a function argument), but it appears that the compiler does not infer this automatically.

I tried your suggestion to use a loop instead, which worked. As an alternative, I replaced

```julia
    ntuple(i -> prod(t[parts[i]+1 : parts[i+1]]), Val(M-1))

```

with

```julia
    ntuple(i -> prod(t[j] for j ∈ t[parts[i]+1]:parts[i+1]), Val(M-1))

```

Thus, “lazy” indexing is the way to go here.
