# Reuse `Tuple` `convert` mechanism in another parametric type

`Tuple`s have this nice behavior:

``````z8 = (Int8(0),Int8(0))
z64 = (0,0)

a8 = Vector{Tuple{Int8, Int8}}()
a64 = Vector{Tuple{Int64, Int64}}()

# these all work
push!(a8, z8)
push!(a64, z64)
push!(a8, z64)
push!(a64, z8)
``````

At first I thought the behavior I wanted was related to `Tuple`s being covariant in their type parameters [1] (which doesn’t make sense because there’s no subtype relationship between `Int8` and `Int64`, and I would also need contravariance at the same time). But instead he behavior I want is due to the `convert` methods defined on `Tuple` [2].

``````# previous `push!`s work because these all work
convert(eltype(a8), z8)
convert(eltype(a64), z64)
convert(eltype(a8), z64)
convert(eltype(a64), z8)
``````

I would like to apply this behavior to another type I define:

``````struct Wrap{T}
x::T
end

w8 = Wrap(z8)
w64 = Wrap(z64)

b8 = Vector{Wrap{Tuple{Int8, Int8}}}()
b64 = Vector{Wrap{Tuple{Int64, Int64}}}()

# I want all of these to work
push!(b8, w8)
push!(b64, w64)
push!(b8, w64)
push!(b64, w8)

# which requires all of these to work.
convert(eltype(b8), w8)
convert(eltype(b64), w64)
convert(eltype(b8), w64)
convert(eltype(b64), w8)
``````

I can get my desired `Wrap` behavior by defining

``````Base.convert(::Type{Wrap{T}}, w::Wrap) where {T} = Wrap(convert(T, w.x))
``````

but I’m hoping there’s an existing idiom for defining a similar method across all fields of a given type.

Related discussion:

Generally you would need a `convert` method.

Are you looking for an easier way to implement that? Is there a more complex example you need help with? I am asking because for the above, what you are doing may be the simplest solution.

I think you understand already, and I’m just looking for a package or some other abbreviated way to do the following:

``````function Base.convert(::Type{Wrap3{A, B, C}}, w::Wrap3) where {A, B, C}
return Wrap3(
convert(A, w.a),
convert(B, w.b),
convert(C, w.c)
)
end
``````

given

``````struct Wrap3{A, B, C}
a::A
b::B
c::C
end
``````

Seems like it might exist in a package as a macro definition.

Another two “features” I’d like:

1. free type parameters
``````convert(Tuple{Int64, A} where A, (1.0, 2.0))
``````

makes a `(1, 2.0)`

Analogously, I’d like to have this work:

``````convert(Wrap3{Int64, Float32, C} where C, Wrap3(1.0, 2, "hey"))
``````
1. type parameters that cannot be inferred from constructor arguments:
I can’t demonstrate this with `Tuple`, but I would hope that whatever mechanism gets me the previously mentioned features would also work as follows
``````struct TaggedWrap3{TAG, A, B, C}
a::A
b::B
c::C
end

# allow construction like: TaggedWrap3{:tag}(1,2,3)
function (::Type{TaggedWrap3{TAG,A,B,C} where {A,B,C}})(a,b,c) where {TAG}
return TaggedWrap3{TAG, typeof(a), typeof(b), typeof(c)}(a,b,c)
end

function Base.convert(::Type{TaggedWrap3{TAG,A,B,C} where {A, B, C}}, w::Wrap3) where {TAG}
return TaggedWrap3{TAG}(w.a, w.b, w.c)
end

function Base.convert(::Type{TaggedWrap3{TAG,A,B,C}}, w::Wrap3) where {TAG, A, B, C}
return TaggedWrap3{TAG}(
convert(A, w.a),
convert(B, w.b),
convert(C, w.c)
)
end

function Base.convert(::Type{TaggedWrap3{Any, A, B, C}}, w::TaggedWrap3{TAG}) where {TAG, A, B, C}
return TaggedWrap3{TAG}(
convert(A, w.a),
convert(B, w.b),
convert(C, w.c)
)
end

@show convert(TaggedWrap3{:tag}, Wrap3(1,2,3))
@show convert(TaggedWrap3{:tag, Float64, Float64, Float64}, Wrap3(1,2,3))
@show convert(TaggedWrap3{Any, Float64, Float64, Float64}, TaggedWrap3{:tag}(1,2,3))
# doesn't work, but should
@show convert(TaggedWrap3{:tag, Float64, Float64, C} where C, Wrap3(1,2,3))
``````

producing

``````convert(TaggedWrap3{:tag}, Wrap3(1, 2, 3)) = TaggedWrap3{:tag,Int64,Int64,Int64}(1, 2, 3)
convert(TaggedWrap3{:tag, Float64, Float64, Float64}, Wrap3(1, 2, 3)) = TaggedWrap3{:tag,Float64,Float64,Float64}(1.0, 2.0, 3.0)
convert(TaggedWrap3{Any, Float64, Float64, Float64}, TaggedWrap3{:tag}(1, 2, 3)) = TaggedWrap3{:tag,Float64,Float64,Float64}(1.0, 2.0, 3.0)
``````

I am not aware of a package like this, but note that the default constructor goes a long way:

``````struct Wrap3{A,B,C}
a::A
b::B
c::C
end

Base.convert(::Type{W}, w::Wrap3) where {W <: Wrap3} = W(w.a, w.b, w.c)

convert(Wrap3{Int,Float64,Int8}, Wrap3(1, 2, 3)) # example
``````

You can do the same for `TaggedWrap`, just use the `W{TAG}` constructor.