# Type unstable function returning a tuple

**URL:** <https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493>\
**Category:** General Usage\
**Created:** [November 3, 2020, 12:40am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493 "2020-11-03T00:40:18Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [November 3, 2020, 12:40am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/1 "2020-11-03T00:40:18Z")

</div>

Good evening,

I have been facing a (slightly) annoying type inference issue when writing a function which returns a tuple.

Here is a minimal working example:

```julia
f(v, ::Val{N}) where {N} = ntuple(i -> sum(v[1]), N) # type unstable
g(v, ::Val{N}) where {N} = ntuple(i -> v[1][1], N) # type stable

```

For instance, set `v = [[1,2,3], [4,5,6]]`.

We have:

```julia
julia> @code_warntype f(v, Val(3))
Variables
  #self#::Core.Compiler.Const(f, false)
  v::Array{Array{Int64,1},1}
  #unused#::Core.Compiler.Const(Val{3}(), false)
  #11::var"#11#12"{Array{Array{Int64,1},1}}

Body::Tuple{Vararg{Int64,N} where N}
1 ─ %1 = Main.:(var"#11#12")::Core.Compiler.Const(var"#11#12", false)
│ %2 = Core.typeof(v)::Core.Compiler.Const(Array{Array{Int64,1},1}, false)
│ %3 = Core.apply_type(%1, %2)::Core.Compiler.Const(var"#11#12"{Array{Array{Int64,1},1}}, false)
│ (#11 = %new(%3, v))
│ %5 = #11::var"#11#12"{Array{Array{Int64,1},1}}
│ %6 = Main.ntuple(%5, $(Expr(:static_parameter, 1)))::Tuple{Vararg{Int64,N} where N}
└── return %6

```

However, `g` is type stable:

```julia
julia> @code_warntype g(v, Val(3))
Variables
  #self#::Core.Compiler.Const(g, false)
  v::Array{Array{Int64,1},1}
  #unused#::Core.Compiler.Const(Val{3}(), false)
  #9::var"#9#10"{Array{Array{Int64,1},1}}

Body::Tuple{Int64,Int64,Int64}
1 ─ %1 = Main.:(var"#9#10")::Core.Compiler.Const(var"#9#10", false)
│ %2 = Core.typeof(v)::Core.Compiler.Const(Array{Array{Int64,1},1}, false)
│ %3 = Core.apply_type(%1, %2)::Core.Compiler.Const(var"#9#10"{Array{Array{Int64,1},1}}, false)
│ (#9 = %new(%3, v))
│ %5 = #9::var"#9#10"{Array{Array{Int64,1},1}}
│ %6 = Main.ntuple(%5, $(Expr(:static_parameter, 1)))::Tuple{Int64,Int64,Int64}
└── return %6

```

In my code, the needed tuple is used in a loop so I resolved this type instability via function barrier.

Although, why the compiler could not find out the length of the tuple returned by `f`? Indeed, it knows what `sum` does and the length `N` is given as a type.

EDIT: I should mention that I am using Julia 1.5.2. Moreover, in case this relates, the function

```julia
h(v::Vector{Vector{Int}}) = ntuple(i -> sum(v[1]), 3)

```

is also type unstable.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 3, 2020, 12:50am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/2 "2020-11-03T00:50:14Z")

</div>

I think that the problem is that the elements of `v` could in principle be of any type. If you construct `v` with a specified element type, what happens?

```julia
v = Vector{Int}[....]

```

Sorry, cannot test now (edit: and looking closer that seems not to be case)

---

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [November 3, 2020, 12:53am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/3 "2020-11-03T00:53:06Z")

</div>

I tried defining

```julia
f(v::Vector{Vector{Int}}, ::Val{N}) where {N} = ntuple(i -> sum(v[1]), N)

```

But this function remains unstable.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 3, 2020, 1:05am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/4 "2020-11-03T01:05:54Z")

</div>

Again, sorry for just guessing now. You may need to guarantee that the elements of v have dimension 1, and for that use static arrays. Or perhaps pass to sum that you only want the sum over the first dimension.

---

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [November 3, 2020, 1:15am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/5 "2020-11-03T01:15:48Z")

</div>

No worries 🙂. Interesting, it seems that you are right; the following function is type stable:

```julia
l(v::Vector{NTuple{N,Int}}, ::Val{M}) where {N,M} = ntuple(i -> sum(v[1]), M)

```

However, I still do not understand the problem with the other function `f` and `h` (cf. edit in original post). The compiler knows that no matter the dimension / length the result of `sum(v[1])` is `Int64`. No?

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 3, 2020, 1:27am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/6 "2020-11-03T01:27:16Z")

</div>

If `v[1]` was a vector of vectors it would return a vector:

```julia
sum([ones(3),ones(3)])

[2.0, 2.0, 2.0]

```

---

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [November 3, 2020, 1:29am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/7 "2020-11-03T01:29:33Z")

</div>

Agreed! However, considering the definition of `h`, Julia knows that `v::Vector{Vector{Int}}` such that `v[1]::Vector{Int}`. No?

In fact, isn’t it what `@code_warntype` shows us, i.e. `Body::Tuple{Vararg{Int64,N} where N}`? Perhaps I do not understand properly how to read this… Julia seems to know that the result is a tuple of `Int64` but does not figure out the length of it. Perhaps the compiler chooses not to specialize these functions on purpose?

For the sake of completeness, in my case the function I use is closer to something like this:

```julia
foo(t::NTuple{N,NTuple{M,Int}}) where {N,M} =
    ntuple(i -> nextpow(2, mapreduce(tj -> tj[i], +, t)), M)

```

Again, to me it seems that everything is known at compile time… yet it is type unstable.

---

<div class="post-metadata">

**Author:** ![Bigorneau](https://avatars.discourse-cdn.com/v4/letter/b/4491bb/32.png) [@Bigorneau](https://discourse.julialang.org/u/Bigorneau)\
**Post date:** [November 3, 2020, 3:23am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/8 "2020-11-03T03:23:52Z")

</div>

So I actually found a trick to fix the type stability issue: use `Val` when calling `ntuple`. The following versions of `h,f,foo` are all type stable:

```julia
h_(v::Vector{Vector{Int}}) = ntuple(i -> sum(v[1]), Val{3}())
f_(v, ::Val{N}) where {N} = ntuple(i -> sum(v[1]), Val{N}())
foo_(t::NTuple{N,NTuple{M,Int}}) where {N,M} =
    ntuple(i -> nextpow(2, mapreduce(tj -> tj[i], +, t)), Val{M}())

```

(take `v = [[1,2,3],[4,5,6]]` and `t = ((1, 2, 3), (4, 5, 6))` for instance)

This does not explain why `h,f,foo` were not type stable though ☹

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [November 3, 2020, 4:27am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/9 "2020-11-03T04:27:14Z")

</div>

`ntuple(f, n::Integer)` is an inherently type unstable function, because the type of the output depends on the _value_ of `n`, rather than on the type of `n`. For example, the return type of `ntuple(i -> 2i, 2)` is `Tuple{Int,Int}`, but the return type of `ntuple(i -> 2i, 3)` is `Tuple{Int,Int,Int}`.

If you call `methods(ntuple)`, you can see that they have provided some type-stable versions of `ntuple`:

```julia
julia> methods(ntuple)
# 6 methods for generic function "ntuple":
[1] ntuple(f::F, n::Integer) where F in Base at ntuple.jl:17
[2] ntuple(f, ::Val{0}) in Base at ntuple.jl:40
[3] ntuple(f, ::Val{1}) in Base at ntuple.jl:41
[4] ntuple(f, ::Val{2}) in Base at ntuple.jl:42
[5] ntuple(f, ::Val{3}) in Base at ntuple.jl:43
[6] ntuple(f::F, ::Val{N}) where {F, N} in Base at ntuple.jl:45

```

However, these type stable methods are not documented in the docstring for `ntuple`…

EDIT: Upon further reflection, maybe that doesn’t explain everything. As you originally asked, why is `g` type stable? I guess when `ntuple` is used inside a function like `f` and `g` above, the compiler sometimes knows that `N` is a constant? 🤔 But it depends on the function provided to `ntuple`??? 🤷‍♂️

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 3, 2020, 10:42am UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/10 "2020-11-03T10:42:02Z")

</div>

> [@Bigorneau](#):
>
> `v = [[1,2,3], [4,5,6]]`

Just to point out that even `f` becomes type-stable if the `v` provided has a defined length:

```julia
julia> f(v, ::Val{N}) where {N} = ntuple(i -> sum(v[1]), N)

julia> v2 = [SVector([1,2,3]...), SVector([1,2,3]...) ]
2-element Array{SArray{Tuple{3},Int64,1,3},1}:
 [1, 2, 3]
 [1, 2, 3]

julia> @code_warntype f(v2,Val(3))
Variables
  #self#::Core.Compiler.Const(f, false)
  v::Array{SArray{Tuple{3},Int64,1,3},1}
  #unused#::Core.Compiler.Const(Val{3}(), false)
  #1::var"#1#2"{Array{SArray{Tuple{3},Int64,1,3},1}}

Body::Tuple{Int64,Int64,Int64}

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 3, 2020, 12:05pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/11 "2020-11-03T12:05:09Z")

</div>

> [@Bigorneau](#):
>
> `f_(v, ::Val{N}) where {N} = ntuple(i -> sum(v[1]), Val{N}())`

You can just write

```julia
f(v, ::Val{N}) where {N} = ntuple(i -> sum(v[1]), Val(N))

```

or, alternatively

```julia
f(v, n::Val{N}) where {N} = ntuple(i -> sum(v[1]), n)

```

Or even:

```julia
f(v, n::Val) = ntuple(i -> sum(v[1]), n)

```

---

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [November 3, 2020, 12:16pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/12 "2020-11-03T12:16:44Z")

</div>

There are helper functions in Unrolled.jl which help with these, in the general case.

```julia
using Unrolled
f(v, ::Val{N}) where {N} = unrolled_map(i->sum(v[1]), @fixed_range 1:N)

```

works. @DNF’s solution is simpler though in this instance.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 3, 2020, 12:18pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/13 "2020-11-03T12:18:01Z")

</div>

> [@Bigorneau](#):
>
> ```julia
> foo_(t::NTuple{N,NTuple{M,Int}}) where {N,M} =
> ntuple(i -> nextpow(2, mapreduce(tj -> tj[i], +, t)), Val{M}())
> 
> ```

I thought I had found a nice solution for this, namely:

```julia
 foo(t) = ntuple(i -> nextpow(2, mapreduce(tj -> tj[i], +, t)), length(t[Val(1)]))

```

since `@code_warntype` gave a nice result. However, apparently, you cannot index with `Val`:

```julia
julia> foo(((1,2,3), (3,4,5), (5,6,7)))
ERROR: MethodError: no method matching getindex(::Tuple{Tuple{Int64,Int64,Int64},Tuple{Int64,Int64,Int64},Tuple{Int64,Int64,Int64}}, ::Val{1})

```

I wonder why that is.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 3, 2020, 12:25pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/14 "2020-11-03T12:25:06Z")

</div>

> [@DNF](#):
>
> `f(v, n::Val) = ntuple(i -> sum(v[1]), n)`

Actually, you should just write

```julia
f(v, n) = ntuple(i -> sum(v[1]), n)

```

As long as you call it with `f(v, Val(3))` it will be type stable.

I like this. Just keep removing type annotations until there’s nothing left, and then you have the right solution.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [November 3, 2020, 12:25pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/15 "2020-11-03T12:25:29Z")

</div>

> [@DNF](#):
>
> I wonder why that is.

Why it should be? `Val` is not a subtype of number, nor any index type.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 3, 2020, 12:26pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/16 "2020-11-03T12:26:59Z")

</div>

Well, `Val(1)` would be very nice to be able to index with. But I see it could be difficult to implement.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [November 3, 2020, 12:41pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/17 "2020-11-03T12:41:49Z")

</div>

I do not think it is difficult…

```julia
Base.getindex(a :: AbstractArray, ::Val{T}) where T = a[T]

```

I just think they are different concepts which probably should be kept separate.

I am not sure if your `Val(1)` in `length(t[Val(1)])` makes any sense, it seems to me the compiler should be able to do any optimization without the call to `Val(1)` and just using `1` instead. Previous code used `Val` together with a function barrier to compile a different and type-stable function for each number of elements in the tuple.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 3, 2020, 12:45pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/18 "2020-11-03T12:45:29Z")

</div>

> [@Henrique\_Becker](#):
>
> I am not sure if your `Val(1)` in `length(t[Val(1)])` makes any sense, it seems to me the compiler should be able to do any optimization without the call to `Val(1)` and just using `1` instead.

Yes, preferably, the compiler should just see this. But as for it making sense: Does

```julia
getindex(a, Val(3))

```

make any less sense than

```julia
ntuple(i->i^2, Val(3))

```

?

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 3, 2020, 12:49pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/19 "2020-11-03T12:49:00Z")

</div>

> [@Henrique\_Becker](#):
>
> `Base.getindex(a :: AbstractArray, ::Val{T}) where T = a[T]`

This removes the error, but doesn’t help with the type instability, btw.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [November 3, 2020, 1:16pm UTC](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493/20 "2020-11-03T13:16:55Z")

</div>

> [@DNF](#):
>
> Does
> 
> ```julia
> getindex(a, Val(3))
> 
> ```
> 
> make any less sense than
> 
> ```julia
> ntuple(i->i^2, Val(3))
> 
> ```
> 
> ?

No, you are correct, it does not. I think someone should make a PR to investigate why `ntuple` does not throw an error when `Val` is passed as second parameter. The documentation is clear that the second parameter should be an `Integer` and `Val(3)` is not.

> [@DNF](#):
>
> This removes the error, but doesn’t help with the type instability, btw.

…

Yes, this is the whole point, this is why I said your try did not make sense; I think I am not getting my point across.

In the other cases, the type-instability did not disappear by some `Val` magic, `Val` is a simple parametric type any of us could define. The type-instability did disappear because the part where it arose was wrapped into a function and the tuple size, which was passed as a value but defined the return type, was instead passed as a type (i.e., wrapped in `Val`). So for the same input types (i.e., same method), we have the same return type. This is what solves the type instability. Consequently, continuing to pass the tuple size as a value and then wrap it in `Val` inside the function has no effect.

[Next page](https://discourse.julialang.org/t/type-unstable-function-returning-a-tuple/49493.md?page=2)
