# How to strip tuple type of the element types

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:

``````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:

``````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> 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}
``````

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.

The fixed-length case is already accurate above:

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

Well,

``````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.

1 Like

My solution.

``````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)
``````
1 Like

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

``````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.

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

``````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:

``````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:

``````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:

``````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}
``````