# Puzzling inference with inhomogeneous tuples

**URL:** <https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498>\
**Category:** General Usage\
**Tags:** question\
**Created:** [February 6, 2019, 7:37am UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498 "2019-02-06T07:37:01Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [February 6, 2019, 7:37am UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/1 "2019-02-06T07:37:01Z")

</div>

Consider this reduced example:

```julia
f(term, sets...) = (sets..., zero(term)), zero(term)

g(sets, term, terms...) = g(_g(sets, term), terms...)
g(sets) = sets
function _g(sets, term)
    newsets, newset = f(term, sets...)
    return newsets
end

function main(terms)
    sets = g((), terms...)
    return sets
end

using StaticArrays
ss = (SVector{2,Int}, SVector{1,Float64}, SVector{2,Int})

```

I want to ensure type-stability of a related (but more complicated) piece of code. But I see this (v1.0.2 and v1.1)

```julia
julia> @code_warntype main(ss)
Body::Tuple
....

```

while `g((), ss...)` that is called by `main` is inferred just fine!

```julia
julia> @code_warntype g((), ss...)
Body::Tuple{SArray{Tuple{2},Int64,1,2},SArray{Tuple{1},Float64,1,1},SArray{Tuple{2},Int64,1,2}}
...

```

Any help to make `main` inferable would be _very_ appreciated!

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [February 6, 2019, 7:58am UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/2 "2019-02-06T07:58:41Z")

</div>

```julia
julia> typeof(ss)
Tuple{DataType,DataType,DataType}

```

If you where given this type for the input `ss` to `main`, could you predict the output?

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [February 6, 2019, 8:07am UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/3 "2019-02-06T08:07:34Z")

</div>

Oh, how interesting! I see the origin of the problem now, thanks! ~~Any solution you can think of? (I can give more details of what I try to do if required.)~~  
EDIT: The solution is obviously to rethink my design 😃

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [February 6, 2019, 9:41am UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/4 "2019-02-06T09:41:24Z")

</div>

Mmm, I think there might be more to this. Following your comment I now make (the equivalent of) `ss` to be concrete-typed in my code. Inference still fails for `main`

```julia
f(term, sets...) = (sets..., term), term

g(sets, term, terms...) = g(_g(sets, term), terms...)
g(sets) = sets
function _g(sets, term)
    newsets, newset = f(term, sets...)
    return newsets
end

function main(terms)
    sets = g((), terms...)
    return sets
end

using StaticArrays
ss = zero.((SVector{2,Int}, SVector{1,Float64}, SVector{2,Int}))

```

```julia
julia> typeof(ss)
Tuple{SArray{Tuple{2},Int64,1,2},SArray{Tuple{1},Float64,1,1},SArray{Tuple{2},Int64,1,2}}

julia> @code_warntype main(ss)
Body::Tuple
...

julia> @code_warntype g((), ss...)
Body::Tuple{SArray{Tuple{2},Int64,1,2},SArray{Tuple{1},Float64,1,1},SArray{Tuple{2},Int64,1,2}}
...

```

Any further suggestion?

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [February 6, 2019, 10:10am UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/5 "2019-02-06T10:10:12Z")

</div>

Seems there is some inference caching problems going on here. If we exectue `@code_warntype g((), ss...)` before `@code_warntype main(ss)` then it infers:

```julia
julia> @code_warntype g((), ss...)
Body::Tuple{SArray{Tuple{2},Int64,1,2},SArray{Tuple{1},Float64,1,1},SArray{Tuple{2},Int64,1,2}}
...
julia> @code_warntype main(ss)
Body::Tuple{SArray{Tuple{2},Int64,1,2},SArray{Tuple{1},Float64,1,1},SArray{Tuple{2},Int64,1,2}}
...

```

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [February 6, 2019, 10:17am UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/6 "2019-02-06T10:17:24Z")

</div>

😮  
That is weird!

If I put the code in a module, however, there is no caching anomaly, only in global REPL scope.

Also: version 0.6 infers this without problem, independent of the order. I guess this could be some lingering bug in the new optimiser?

I’ll open an issue.

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [February 6, 2019, 10:56am UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/7 "2019-02-06T10:56:07Z")

</div>

[https://github.com/JuliaLang/julia/issues/30976](https://github.com/JuliaLang/julia/issues/30976)

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [February 6, 2019, 11:13am UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/8 "2019-02-06T11:13:48Z")

</div>

For anyone interested, I found a workaround that seems to be good enough. It involves _not_ starting with an empty tuple in the first `g` call inside `main`. This is what seems to trip inference. The following equivalent version infers correctly

```julia
f(newset, sets...) = (sets..., newset), newset
g(term, terms...) = _g(first(f(term)), terms...)
_g(sets, term, terms...) = _g(_g(sets, term), terms...)
_g(sets, term) = first(f(term, sets...))
_g(sets) = sets

function main(terms)
    sets = g(terms...)
    return sets
end

using StaticArrays
ss = zero.((SVector{2,Int}, SVector{1,Float64}, SVector{2,Int}))

```

```julia
julia> @code_warntype main(ss)
Body::Tuple{SArray{Tuple{1},Float64,1,1},SArray{Tuple{2},Int64,1,2},SArray{Tuple{2},Int64,1,2},SArray{Tuple{1},Float64,1,1},SArray{Tuple{2},Int64,1,2}}
...

```

This seems to be good enough for my real-world case, so I’ll mark this as solved, although there is probably still a bug in the Julia inference code, somewhere.

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [February 6, 2019, 3:42pm UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/9 "2019-02-06T15:42:46Z")

</div>

Just wanted to pop by and thank @kristoffer.carlsson once more. I was really stuck with a real-world problem due to this, and his first comment was crucial to solve it. What a difference do such things make in Julia! But geez, it takes some serious training to get the hang of it. I sometime feel Julia is truly a language for academics. Great fun, powerful, but deep and subtle as hell!

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [February 6, 2019, 3:48pm UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/10 "2019-02-06T15:48:27Z")

</div>

> [@pablosanjose](#):
>
> But geez, it takes some serious training to get the hang of it. I sometime feel Julia is truly a language for academics. Great fun, powerful, but deep and subtle as hell!

This is “just” a bug though.

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [February 6, 2019, 3:56pm UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/11 "2019-02-06T15:56:34Z")

</div>

Yes, the second bit is indeed a bug. I was referring to your first comment about dispatching with tuples of `Type`s. I was naively expecting I could, but:

```julia
julia> f(::Tuple{Type{S},Type{T}}) where {S,T} = 2
f (generic function with 1 method)
julia> f(::Tuple{Type{S},Type{S}}) where {S} = 1
f (generic function with 2 methods)
julia> f((Int,Float64))
2
julia> f((Int,Int))
2

```

I found that really puzzling. It of course makes sense after your comment, but I find it subtle!

```julia
julia> typeof((Int,Float64)) === typeof((Int,Int))
true

```

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [February 6, 2019, 4:03pm UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/12 "2019-02-06T16:03:41Z")

</div>

Ah, tuple types are written using the `Tuple{...}` syntax. They aren’t really normal tuples though.

```julia
julia> Tuple{Int, Int}
Tuple{Int64,Int64}

```

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [February 6, 2019, 4:16pm UTC](https://discourse.julialang.org/t/puzzling-inference-with-inhomogeneous-tuples/20498/13 "2019-02-06T16:16:20Z")

</div>

I see. I was wanting to use proper tuples to be able to splat two “tuples of types” into one and similar things. Which I guess you cannot do with “tuple types”. Anyway, I ended doing this, which I find quite cool

```julia
julia> struct TypeWrapper{T}
       end
julia> TypeWrapper(::Type{T}) where {T} = TypeWrapper{T}()
TypeWrapper
julia> f(::Tuple{TypeWrapper{T}, TypeWrapper{S}}) where {S,T} = 2
f (generic function with 1 method)
julia> f(::Tuple{TypeWrapper{S}, TypeWrapper{S}}) where {S} = 1
f (generic function with 2 methods)
julia> f((TypeWrapper(Int), TypeWrapper(Int)))
1
julia> f((TypeWrapper(Int), TypeWrapper(Float64)))
2

```
