# How to strip tuple type of the element types

**URL:** <https://discourse.julialang.org/t/how-to-strip-tuple-type-of-the-element-types/109664>\
**Category:** General Usage\
**Tags:** type, tuple, parametric-types, function, ntuple\
**Created:** [February 3, 2024, 11:50am UTC](https://discourse.julialang.org/t/how-to-strip-tuple-type-of-the-element-types/109664 "2024-02-03T11:50:58Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [February 3, 2024, 11:50am UTC](https://discourse.julialang.org/t/how-to-strip-tuple-type-of-the-element-types/109664/1 "2024-02-03T11:50:58Z")

</div>

I want a function taking a type `T<:Tuple` type and returning `S<:Tuple` type such that `T<:S` and the element types are stripped from the input, but with preserved length information.

Desired behavior examples:

```julia
f(Tuple{}) == Tuple{}
f(Tuple{Int,String}) == f(Tuple{X,X} where {X<:Number}) == Tuple{Any,Any}
f(Tuple{Int,Vararg{String}}) == Tuple{Any,Vararg}
f(Union{Tuple{Int},Tuple{String}}) == Tuple{Any}

```

Any ideas for implementing this?

Here’s an approximation:

```julia
const Tuple1OrMore = Tuple{Any, Vararg}
const Tuple2OrMore = Tuple{Any,Any, Vararg}
const Tuple3OrMore = Tuple{Any,Any,Any, Vararg}
const Tuple4OrMore = Tuple{Any,Any,Any,Any, Vararg}
const Tuple5OrMore = Tuple{Any,Any,Any,Any,Any,Vararg}

g(::Type{<:Tuple }) = Tuple
g(::Type{<:Tuple1OrMore}) = Tuple1OrMore
g(::Type{<:Tuple2OrMore}) = Tuple2OrMore
g(::Type{<:Tuple3OrMore}) = Tuple3OrMore
g(::Type{<:Tuple4OrMore}) = Tuple4OrMore
g(::Type{<:Tuple5OrMore}) = Tuple5OrMore

f(::Type{T}) where {len, T<:NTuple{len,Any}} = NTuple{len,Any}
f(::Type{T}) where {T<:Tuple} = g(T)

```

```julia-repl
julia> f(Tuple{})
Tuple{}

julia> f(Tuple{Int,String})
Tuple{Any, Any}

julia> f(Tuple{Int,Vararg{String}})
Tuple{Any, Vararg}

julia> f(Union{Tuple{Int},Tuple{String}})
Tuple{Any}

```

---

<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:** [February 3, 2024, 12:39pm UTC](https://discourse.julialang.org/t/how-to-strip-tuple-type-of-the-element-types/109664/2 "2024-02-03T12:39:56Z")

</div>

For fixed-length tuple type without `Vararg`, `f(T) = NTuple{fieldcount(T)}` or `f(T) = NTuple{fieldcount(T), Any}` should work – depending on what you need exactly.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [February 3, 2024, 12:41pm UTC](https://discourse.julialang.org/t/how-to-strip-tuple-type-of-the-element-types/109664/3 "2024-02-03T12:41:41Z")

</div>

The fixed-length case is already accurate above:

> [@nsajko](#):
>
> `f(::Type{T}) where {len, T<:NTuple{len,Any}} = NTuple{len,Any}`

So I’m specifically interested in the `Vararg` case.

---

<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:** [February 3, 2024, 12:50pm UTC](https://discourse.julialang.org/t/how-to-strip-tuple-type-of-the-element-types/109664/4 "2024-02-03T12:50:54Z")

</div>

Well,

```julia
g(::Type) = Any
g(::typeof(Vararg)) = Vararg
f(T) = Tuple{map(g, T.parameters)...}

```

should work for all tuple types, not for `Union`s though.

---

<div class="post-metadata">

**Author:** ![Tarny\_GG\_Channie](https://avatars.discourse-cdn.com/v4/letter/t/3bc359/32.png) [@Tarny\_GG\_Channie](https://discourse.julialang.org/u/Tarny_GG_Channie)\
**Post date:** [February 3, 2024, 1:00pm UTC](https://discourse.julialang.org/t/how-to-strip-tuple-type-of-the-element-types/109664/5 "2024-02-03T13:00:10Z")

</div>

My solution.

```julia
concatenate_tuple_type(::Type{A},::Type{B}) where {A<:Tuple, B<:Tuple} = Tuple{A.parameters...,B.parameters...}
test_vararg(::typeof(Vararg)) = true
test_vararg(x::Type) = false
test_union(x::Union) = true
test_union(x::Type) = false

f(::Type{Tuple{}}) = Tuple{}
f(::Type{A}) where {A<:Tuple} = test_union(A) ? Union{f(A.a),f(A.b)} : (test_vararg(A.parameters[end]) ? concatenate_tuple_type(NTuple{length(A.parameters)-1,Any},Tuple{Vararg}) : NTuple{length(A.parameters),Any})
println(Tuple{Int,Int}.parameters)

```

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [February 3, 2024, 1:56pm UTC](https://discourse.julialang.org/t/how-to-strip-tuple-type-of-the-element-types/109664/6 "2024-02-03T13:56:33Z")

</div>

Interesting, however I’ll need to adapt it further to make it work for `UnionAll`:

```julia-repl
julia> Tuple{X,X} where {X<:Number}
Tuple{X, X} where X<:Number

julia> f(ans)
ERROR: type UnionAll has no field parameters

```

Also, I’ll probably replace the predicates with traits, I think that would help prevent run time dispatch for long tuple types/deep recursion.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [February 3, 2024, 9:09pm UTC](https://discourse.julialang.org/t/how-to-strip-tuple-type-of-the-element-types/109664/7 "2024-02-03T21:09:57Z")

</div>

A solution, it’s quite nice and doesn’t use implementation details, like the `parameters` field:

```julia
h(::V) where {V<:Val} = Tuple{ntuple(Returns(Any), V())..., Vararg}

function g(::Type{T}, ::Val{n}) where {T<:Tuple,n}
  if T <: h(Val(n))
    g(T, Val(n + 1))::Type{<:Tuple}
  else
    h(Val(n - 1))
  end
end

f(::Type{T}) where {T<:Tuple} = g(T, Val(1))::Type{<:Tuple}

f(::Type{T}) where {len,T<:NTuple{len,Any}} = NTuple{len,Any}

```

A variant:

```julia
h(::V) where {V<:Val} = Tuple{ntuple(Returns(Any), V())..., Vararg}

function g(::Type{T}, ::Val{n}) where {T<:Tuple,n}
  if T <: h(Val(n))
    g(T, Val(n + 1))::Val
  else
    Val(n - 1)
  end
end

f(::Type{T}) where {T<:Tuple} = h(g(T, Val(1)))::Type{<:Tuple}

f(::Type{T}) where {len,T<:NTuple{len,Any}} = NTuple{len,Any}

```

With branching replaced by dispatch:

```julia
h(::V) where {V<:Val} = Tuple{ntuple(Returns(Any), V())..., Vararg}

g(::Type{T}, ::Val{n}, ::Type{S}) where {T<:Tuple,n,S<:Tuple} = Val(n - 1)
function g(::Type{T}, ::Val{n}, ::Type{S}) where {n,S<:Tuple,T<:S}
  v = Val(n + 1)
  g(T, v, h(v))::Val
end

function f(::Type{T}) where {T<:Tuple}
  v = Val(1)
  h(g(T, v, h(v)))::Type{<:Tuple}
end

f(::Type{T}) where {len,T<:NTuple{len,Any}} = NTuple{len,Any}

```

With `ntuple` replaced by more recursion:

```julia
ntuple_any(::Val{0}) = ()
function ntuple_any(::Val{n}) where {n}
    (n::Int ≤ 0) && error("unexpected")
    m = n - 1
    t = ntuple_any(Val(m))::NTuple{m,DataType}
    (Any, t...)::NTuple{n,DataType}
end
h(::V) where {V<:Val} = Tuple{ntuple_any(V())..., Vararg}

g(::Type{T}, ::Val{n}, ::Type{S}) where {T<:Tuple,n,S<:Tuple} = Val(n - 1)
function g(::Type{T}, ::Val{n}, ::Type{S}) where {n,S<:Tuple,T<:S}
  v = Val(n + 1)
  g(T, v, h(v))::Val
end

function f(::Type{T}) where {T<:Tuple}
  v = Val(1)
  h(g(T, v, h(v)))::Type{<:Tuple}
end

f(::Type{T}) where {len,T<:NTuple{len,Any}} = NTuple{len,Any}

```
