# Type inference with a tuple slice

**URL:** <https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650>\
**Category:** General Usage\
**Tags:** type\
**Created:** [January 22, 2020, 2:53am UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650 "2020-01-22T02:53:40Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)\
**Post date:** [January 22, 2020, 2:53am UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/1 "2020-01-22T02:53:40Z")

</div>

Hi,

I’m playing around with NTuple to better familiarize myself with variable length function argument etc. In the process, I encountered an example where type inference failed. Here is a minimal example.

```julia
function test()
    tuple = (2,2,2)
    return tuple[1:2]
end

@code_warntype test()

```

This outputs

```julia
@code_warntype BLG_DMRG.test()
Variables
  #self#::Core.Compiler.Const(BLG_DMRG.test, false)
  tuple::Tuple{Int64,Int64,Int64}

Body::Tuple{Vararg{Int64,N} where N}
1 ─ (tuple = Core.tuple(2, 2, 2))
│ %2 = tuple::Core.Compiler.Const((2, 2, 2), false)::Core.Compiler.Const((2, 2, 2), false)
│ %3 = (1:2)::Core.Compiler.Const(1:2, false)
│ %4 = Base.getindex(%2, %3)::Tuple{Vararg{Int64,N} where N}
└── return %4

```

Even though the compiler knows the value of the slice (1:2), it still cannot figure out the length of the output tuple. Why is that the case, and is there a way to make the type inference successful?

(This issue came up when I was playing around with the following code, which contracts the last index of the first tensor with the first index of the second tensor. The output type is determined by input types (i.e. given Array(N) and Array(M), output is Array(N+M-2)), but the compiler cannot seem to figure out, due to a similar tuple type inference failure. I added the result of @code\_warntype for this at the bottom.)

```julia
function contract_last_and_first(W::AbstractArray, Z::AbstractArray)
    dimensions1 = size(W)
    dimensions2 = size(Z)
    W2 = reshape(W, prod(dimensions1[1:N-1]), dimensions1[end])
    Z2 = reshape(Z, dimensions2[1], prod(dimensions2[2:M]))
    Tensor = W2 * Z2
    reshape(Tensor, (dimensions1[1:end-1]...,dimensions2[2:end]...))
end

W = rand(3,3,3)
Z = rand(3,3,3,3)
@code_warntype(W,Z)

```

```julia
@code_warntype contract_last_and_first(W,Z)
Variables
  #self#::Core.Compiler.Const(contract_last_and_first, false)
  W::Array{Float64,3}
  Z::Array{Float64,4}
  dimensions1::Tuple{Int64,Int64,Int64}
  dimensions2::NTuple{4,Int64}
  W2::Array{Float64,2}
  Z2::Array{Float64,2}
  Tensor::Array{Float64,2}

Body::Array
1 ─ (dimensions1 = size(W))
│ (dimensions2 = size(Z))
│ %3 = dimensions1::Tuple{Int64,Int64,Int64}
│ %4 = (N - 1)::Any
│ %5 = (1:%4)::Any
│ %6 = Base.getindex(%3, %5)::Any
│ %7 = prod(%6)::Any
│ %8 = dimensions1::Tuple{Int64,Int64,Int64}
│ %9 = Base.lastindex(dimensions1)::Core.Compiler.Const(3, false)
│ %10 = Base.getindex(%8, %9)::Int64
│ (W2 = reshape(W, %7, %10))
│ %12 = Base.getindex(dimensions2, 1)::Int64
│ %13 = dimensions2::NTuple{4,Int64}
│ %14 = (2:M)::Any
│ %15 = Base.getindex(%13, %14)::Any
│ %16 = prod(%15)::Any
│ (Z2 = reshape(Z, %12, %16))
│ (Tensor = W2 * Z2)
│ %19 = Tensor::Array{Float64,2}
│ %20 = dimensions1::Tuple{Int64,Int64,Int64}
│ %21 = Base.lastindex(dimensions1)::Core.Compiler.Const(3, false)
│ %22 = (%21 - 1)::Core.Compiler.Const(2, false)
│ %23 = (1:%22)::Core.Compiler.Const(1:2, false)
│ %24 = Base.getindex(%20, %23)::Tuple{Vararg{Int64,N} where N}
│ %25 = dimensions2::NTuple{4,Int64}
│ %26 = Base.lastindex(dimensions2)::Core.Compiler.Const(4, false)
│ %27 = (2:%26)::Core.Compiler.Const(2:4, false)
│ %28 = Base.getindex(%25, %27)::Tuple{Vararg{Int64,N} where N}
│ %29 = Core._apply(Core.tuple, %24, %28)::Tuple{Vararg{Int64,N} where N}
│ %30 = reshape(%19, %29)::Array
└── return %30

```

Thanks in advance!

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [January 22, 2020, 7:02am UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/2 "2020-01-22T07:02:25Z")

</div>

While we are waiting for the explanation, you may actually circumvent this problem by giving hint to compiler (actually, I was surprised that you can pass function to the type definition without any performance issues)

```julia
function test()
    tuple = (2,2,2)
    return tuple[1:2]
end

function test2()
    test() .+ test()
end

function testa()
    tuple = (2,2,2)
    return tuple[1:2]::NTuple{length(1:2), Int}
end

function test2a()
    testa() .+ testa()
end

function testb()
    tuple = (2,2,2)
    return tuple[1:2]::NTuple{2, Int}
end

function test2b()
    testb() .+ testb()
end

```

I used `test2*` functions in order to see problems propagation

```julia
@code_warntype test2()
Body::Any
1 ─ %1 = Main.test()::Tuple{Vararg{Int64,N} where N}
│ %2 = Main.test()::Tuple{Vararg{Int64,N} where N}
│ %3 = Base.broadcasted(Main.:+, %1, %2)::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple},Nothing,typeof(+),_A} where _A<:Tuple
│ %4 = Base.materialize(%3)::Any
└── return %4

@code_warntype test2a()
Body::Tuple{Int64,Int64}
1 ─ %1 = Main.testa()::Tuple{Int64,Int64}
│ %2 = Main.testa()::Tuple{Int64,Int64}
│ %3 = Base.broadcasted(Main.:+, %1, %2)::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple},Nothing,typeof(+),Tuple{Tuple{Int64,Int64},Tuple{Int64,Int64}}}
│ %4 = Base.materialize(%3)::Tuple{Int64,Int64}
└── return %4

```

And performance

```julia
@btime test2() # 1.385 μs (7 allocations: 384 bytes)
@btime test2a() # 967.667 ns (4 allocations: 256 bytes)
@btime test2b() # 972.800 ns (4 allocations: 256 bytes)

```

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [January 22, 2020, 8:34am UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/3 "2020-01-22T08:34:03Z")

</div>

You may also consider using the Base functions `first, tail` and sometimes the odd `reverse(Base.tail(reverse(tup)))`. These are type stable

---

<div class="post-metadata">

**Author:** ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)\
**Post date:** [January 22, 2020, 8:09pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/4 "2020-01-22T20:09:29Z")

</div>

`NTuple{length(1:2), Int` is a pretty neat trick! Thanks! For now I will use type hinting as suggested.

---

<div class="post-metadata">

**Author:** ![tomohiro\_soejima](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomohiro_soejima/32/8056_2.png) [@tomohiro\_soejima](https://discourse.julialang.org/u/tomohiro_soejima)\
**Post date:** [January 22, 2020, 8:14pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/5 "2020-01-22T20:14:43Z")

</div>

Ah thanks! Looks like first, last, Base.tail, Base.front are exactly what I need for the particular use case I have in mind.

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [February 9, 2020, 4:49pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/6 "2020-02-09T16:49:16Z")

</div>

For future references. Instead of `reverse`, `tail` trick, one may use more straightforward approach with [`@generated`](https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions-1) functions

```julia
@generated function droplast(t::NTuple{N}) where N
    ex = :()
    for i in 1:N-1
        ex = :($ex..., t[$i])
    end
    return ex
end

droplast((1, 2, 3, 4)) # (1, 2, 3)

```

It is type stable and fast

```julia
@code_typed droplast((1, 2, 3, 4))

CodeInfo(
1 ─ %1 = Base.getfield(t, 1, true)::Int64
│ %2 = Base.getfield(t, 2, true)::Int64
│ %3 = Base.getfield(t, 3, true)::Int64
│ %4 = Core.tuple(%1, %2, %3)::Tuple{Int64,Int64,Int64}
└── return %4
) => Tuple{Int64,Int64,Int64}

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [February 9, 2020, 4:52pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/7 "2020-02-09T16:52:58Z")

</div>

> [@Skoffer](#):
>
> more straightforward approach with [`@generated`](https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions-1) functions

Generally one would avoid generated functions whenever possible (and here it is possible). See

> [@Why not use @generated?](https://discourse.julialang.org/t/why-not-use-generated/29531/4):
>
> Generated functions are an escape hatch that allows you to bypass the regular multiple dispatch mechanism. As such, you should only use them if regular multiple dispatch can’t get you what we need. Since multiple dispatch is a very powerful abstraction, you should think carefully about whether your problem is of sufficient complexity that it is beyond the reach of regular multiple dispatch. If you use generated functions, the compiler will have less information about what the function is going t…

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [February 9, 2020, 4:57pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/8 "2020-02-09T16:57:50Z")

</div>

Thank you for the link! It is very informative. But how one can avoid generated functions in this case, except of reverse/tail trick which looks odd?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [February 9, 2020, 5:18pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/9 "2020-02-09T17:18:50Z")

</div>

It may look unfamiliar, but it is a very idiomatic solution for these kind of problems. Check out

> **[GitHub - mitmath/18S096: 18.S096 three-week course at MIT](https://github.com/mitmath/18S096)**
>
> 18.S096 three-week course at MIT. Contribute to mitmath/18S096 development by creating an account on GitHub.

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [February 9, 2020, 5:37pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/10 "2020-02-09T17:37:38Z")

</div>

> [@Skoffer](#):
>
> But how one can avoid generated functions in this case, except of reverse/tail trick which looks odd?

Just writing it explicitly with `ntuple` often works fine, these seem pretty much indistinguishable:

```julia
droplast2(t) = ntuple(i -> t[i], length(t)-1)

droplast3(t) = reverse(Base.tail(reverse(t)))

@code_typed droplast2((1, 2, 3, 4)) 

```

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [February 9, 2020, 6:23pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/11 "2020-02-09T18:23:19Z")

</div>

`ntuple` is amazing! Thank you!

---

<div class="post-metadata">

**Author:** ![1115](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/1115/32/4465_2.png) [@1115](https://discourse.julialang.org/u/1115)\
**Post date:** [June 29, 2021, 5:41pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/12 "2021-06-29T17:41:56Z")

</div>

In one of my real application (a recursive function), I find the performance

`ntuple` \> `indexing, e.g. t[2:end]` \> apply multiple `Base.tail`.

The code is type stable (but Cthulhu shows this function is deoptimized). It is wield the `Base.tail` is so slow.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [August 6, 2021, 7:54pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/13 "2021-08-06T19:54:34Z")

</div>

Just put an `@inline` in front of it. Keep in mind that these manipulations are intended for reasonably short tuples, and your compile times will grow large if you try this long tuples.

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [August 6, 2021, 10:20pm UTC](https://discourse.julialang.org/t/type-inference-with-a-tuple-slice/33650/14 "2021-08-06T22:20:07Z")

</div>

Also note that this kind of tuple slicing should infer much better in 1.7, so ideally these tricks should not be needed anymore in this case.
