# Parametric type to force common container type

**URL:** <https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904>\
**Category:** General Usage\
**Tags:** parametric-types\
**Created:** [March 10, 2021, 7:02pm UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904 "2021-03-10T19:02:28Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![wsshin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wsshin/32/360_2.png) [@wsshin](https://discourse.julialang.org/u/wsshin)\
**Post date:** [March 10, 2021, 7:02pm UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/1 "2021-03-10T19:02:29Z")

</div>

I want to define a type that holds one vector of `Float64` and another of `ComplexF64`, but I want their containers are of the same type. For example, I want them to be `Vector{Float64}` and `Vector{ComplexF64}`, or `CUDA.CuVector{Float64}` and `CUDA.CuVector{ComlpexF64}`, etc.

Initially I hoped something like this to work:

```julia
struct MyWrongType{V<:AbstractVector}
    vr::V{Float64}
    vc::V{ComplexF64}

    MyWrongType{V}(r,c,n) where {V<:AbstractVector} = new(fill(r,n), fill(c,n))
end

```

but this generates an error (related threads: [Cannot define parametric type with parametric field](https://discourse.julialang.org/t/cannot-define-parametric-type-with-parametric-field/22016), [Type definition](https://discourse.julialang.org/t/type-definition/17027)):

```julia
ERROR: TypeError: in Type{...} expression, expected UnionAll, got a value of type TypeVar

```

A working approach is something like

```julia
struct MyType{VR<:AbstractVector{Float64},VC<:AbstractVector{ComplexF64}}
    vr::VR
    vc::VC

    MyType{VR,VC}(r,c,n) where {VR<:AbstractVector{Float64},VC<:AbstractVector{ComplexF64}} = new(fill(r,n), fill(c,n))
end

```

but this does not force `VR` and `VC` to be of the same container type.

The most satisfactory solution I came up with is to use the above working approach and additionally to define an outer constructor that takes the container type `V` as a constructor argument (rather than a type parameter):

```julia
MyType(V, r, c, n) = MyType{V{Float64},V{ComplexF64}}(r,c,n)

```

Then I can do things like

```julia
using StaticArrays, CUDA

m_vector = MyType(Vector, 0.0, 0.0im, 10)
m_static = MyType(SVector{10}, 0.0, 0.0im, 10)
m_cuda = MyType(CuVector, 0.0, 0.0im, 10)

```

Each of these instances holds real and complex vectors of the same container type.

I’m considering taking this approach, but wondering if there is a better, more standard approach to solve the described problem.

---

<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:** [March 11, 2021, 8:40am UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/2 "2021-03-11T08:40:01Z")

</div>

> [@wsshin](#):
>
> define a type that holds one vector of `Float64` and another of `ComplexF64` , but I want their containers are of the same type

I am not sure _why_ you want to do this (I expect no performance or other benefit).

I would just go with something like

```julia
struct MyType{VR,VC}
    vr::VR
    vc::VC
end

```

and define outer constructors to use `Vector`, `SVector`, etc.

---

<div class="post-metadata">

**Author:** ![wsshin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wsshin/32/360_2.png) [@wsshin](https://discourse.julialang.org/u/wsshin)\
**Post date:** [March 11, 2021, 1:40pm UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/3 "2021-03-11T13:40:31Z")

</div>

> [@Tamas\_Papp](#):
>
> I am not sure _why_ you want to do this (I expect no performance or other benefit).
> 
> I would just go with something like
> 
> ```julia
> struct MyType{VR,VC}
> vr::VR
> vc::VC
> end
> 
> ```
> 
> and define outer constructors to use `Vector` , `SVector` , etc.

Because I don’t want to define those individual outer constructors for different container types. You don’t want to push new code to the repository and ask the users to pull it whenever you need to support a new container type.

---

<div class="post-metadata">

**Author:** ![carstenbauer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/carstenbauer/32/4981_2.png) [@carstenbauer](https://discourse.julialang.org/u/carstenbauer)\
**Post date:** [March 11, 2021, 1:45pm UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/4 "2021-03-11T13:45:35Z")

</div>

I guess it would be nice if one could write:

```julia
struct MyType{T, C<:AbstractVector, VR<:C{T}, VC<:C{Complex{T}}}
    vr::VR
    vc::VC
end

```

(Only the `T` part works currently.)

Update: Hm, thinking about it, this probably isn’t possible because `AbstractVector` is `AbstractVector{X} where X`. So `C` would never be just, say, `Vector` but `Vector{X}`.)

---

<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:** [March 11, 2021, 1:55pm UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/5 "2021-03-11T13:55:14Z")

</div>

> [@wsshin](#):
>
> Because I don’t want to define those individual outer constructors for different container types.

The problem is that in general, this is not feasible or even well-defined. See the discussions related to `Base.similar`, which solves a similar (😉) problem.

> [@wsshin](#):
>
> You don’t want to push new code to the repository and ask the users to pull it whenever you need to support a new container type.

This, again, is not possible, without a common API for constructing `<:AbstractVector` instances, which does not exist at the moment. Eg as you have noticed, `SVector`s require the length in the type.

---

<div class="post-metadata">

**Author:** ![wsshin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wsshin/32/360_2.png) [@wsshin](https://discourse.julialang.org/u/wsshin)\
**Post date:** [March 11, 2021, 2:18pm UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/6 "2021-03-11T14:18:02Z")

</div>

> [@Tamas\_Papp](#):
>
> > [@wsshin](#):
> >
> > You don’t want to push new code to the repository and ask the users to pull it whenever you need to support a new container type.
> 
> This, again, is not possible, without a common API for constructing `<:AbstractVector` instances, which does not exist at the moment. Eg as you have noticed, `SVector` s require the length in the type.

It _is_ possible. Not sure if you read my original posting till the end, but the point is that I’ve found a workaround to solve this problem. I wanted to share my solution with other people as I saw similar questions multiple times in this forum and elsewhere. I am happy with my solution, but was just wondering if there is a better, more standard approach.

---

<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:** [March 11, 2021, 2:36pm UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/7 "2021-03-11T14:36:09Z")

</div>

Your solution just happens to work for `SVector` because the type signature is aligned that way, ie `SVector{10}{Float64}` happens to be meaningful.

But if someone had a type where the parameter list happens to be arranged differently (which is perfectly possible, as [the interface](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-array) does not require this), it wouldn’t work, which is why it is not generic.

---

<div class="post-metadata">

**Author:** ![wsshin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wsshin/32/360_2.png) [@wsshin](https://discourse.julialang.org/u/wsshin)\
**Post date:** [March 11, 2021, 2:43pm UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/8 "2021-03-11T14:43:19Z")

</div>

> [@Tamas\_Papp](#):
>
> Your solution just happens to work for `SVector` because the type signature is aligned that way, ie `SVector{10}{Float64}` happens to be meaningful.
> 
> But if someone had a type where the parameter list happens to be arranged differently (which is perfectly possible, as [the interface](https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-array) does not require this), it wouldn’t work, which is why it is not generic.

This is true, but the users can always define a type alias such that the last type parameter is an element type as

```julia
const AliasType{...,T} = OriginalType{...,T,...}

```

where `T` is the element type. For example, `SMatrix{S1,S2,T,L}` is in such a problematic format, but we can define

```julia
const MySMatrix{S1,S2,L,T} = SMatrix{S1,S2,T,L}

```

The user can call `MyType` with `V = MySMatrix{3,3,9}` when `SMatrix` needs to be used.

---

<div class="post-metadata">

**Author:** ![cgeoga](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cgeoga/32/216186_2.png) [@cgeoga](https://discourse.julialang.org/u/cgeoga)\
**Post date:** [March 11, 2021, 4:11pm UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/9 "2021-03-11T16:11:20Z")

</div>

Do you need the types to be the same for some kind of compiler optimization or anything? This doesn’t really solve your problem so much as sidestep it, but if you wanted to avoid very ornate type system games, you could always just put an assert in the constructor that enforces `vc` and `vr` being the same container type.

---

<div class="post-metadata">

**Author:** ![ivirshup](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ivirshup/32/16364_2.png) [@ivirshup](https://discourse.julialang.org/u/ivirshup)\
**Post date:** [March 12, 2021, 6:00am UTC](https://discourse.julialang.org/t/parametric-type-to-force-common-container-type/56904/10 "2021-03-12T06:00:34Z")

</div>

```julia
struct Foo{VT, ET, FV, CV}
    fv::FV
    cv::CV

    function Foo(fv::FV, cv::CV) where {FV, CV}
        VT = typejoin(FV, CV)
        @assert !isabstracttype(VT)
        @assert VT <: AbstractVector
        ET = eltype(CV)
        @assert eltype(CV) === Complex{eltype(FV)}
        new{VT, ET, FV, CV}(fv, cv)
    end
end

```

Should something like this work? It’ll have a long repr, but you can do things like:

```julia
using SparseArrays

f = Foo([1., 2., 3.], ComplexF64[1., 2., 3.])

@assert f isa Foo{Vector, Float64}

sf = Foo(sprand(Float32, 10, .1), sprand(ComplexF32, 10, .2))

@assert sf isa Foo{<:SparseVector, Float32}

```

There should be no downsides to type inference, and the inner constructor pretty much compiles away.
