# Deparametrising types

**URL:** https://discourse.julialang.org/t/deparametrising-types/41939
**Category:** General Usage
**Tags:** question, parametric-types
**Created:** [June 23, 2020, 2:36pm UTC](https://discourse.julialang.org/t/deparametrising-types/41939 "2020-06-23T14:36:35Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)
#### Post date: [June 23, 2020, 2:36pm UTC](https://discourse.julialang.org/t/deparametrising-types/41939/1 "2020-06-23T14:36:36Z")

</div>

I’m running into trouble having parametric types that I would sometimes like to refer to by their deparametrised `UnionAll` versions, while only having instances of the types available.

Here’s a heavily simplified mock-up of what I’m trying to do:

```julia
abstract type AbstractContainer end

struct ContainerType1{T} <: AbstractContainer where T <: Any
    loot::T
end

struct ContainerType2{T1, T2} <: AbstractContainer where T <: Any
    loot1::T1
    loot2::T2
end

unpack(c::ContainerType1) = (c.loot,)
unpack(c::ContainerType2) = (c.loot1, c.loot2)

```

Please don’t take this too literally: There should be more than 2 subtypes of `AbstractContainer`, and they aren’t all specified by simply how many objects they hold. The point here is the type hierarchy.

There are two, related things I would like to do with these types, that I haven’t figured out a good way to do. First there’s this:

```julia
symbolize_contents(x::T) where {T <: AbstractContainer} = T(map(Symbol, unpack(x))...)
symbolize_contents(ContainerType1("1"))

```

This fails, because there is no constructor `ContainerType1{String}(loot::Symbol)`, whereas what I “mean” is to call `ContainerType1(loot::Symbol)`. The best solution I’ve come up with is to define something like this for each container type,

```julia
deparametrize(::Type{<:ContainerType1}) = ContainerType1

```

after which I can do

```julia
symbolize_contents(x::T) where {T <: AbstractContainer} = deparametrize(T)(Symbol(x.loot))

```

This seems a little silly to me, and more importantly, it doesn’t solve the second thing I’m trying to do, which is to generalise the following to all subtypes of `AbstractContainer`:

```julia
merge_containers(x::ContainerType1, y::ContainerType1) = blahblah
merge_containers(x::ContainerType2, y::ContainerType2) = blahblah

```

What I’m trying to say here is that I don’t ever want to merge an instance of `ContainerType1` with an instance of `ContainerType2`, but I am fine merging say a `ContainerType1{String}` with a `ContainerType2{Int}`. So I would like something like

```julia
merge_containers(x::T, y::T) where {T <: AbstractContainer} = blahblah

```

but with the diagonality happening on the level of deparametrised `UnionAll` types, rather than the  
fully specified concrete types. In other words, ideally, I would want to write

```julia
merge_containers(x::T1, y::T2) where {deparametrize(T1) == deparametrize(T2) <: AbstractContainer} = blahblah

```

or maybe

```julia
unionall_containers = (ContainerType1, ContainerType2)
merge_containers(x::T, y::T) where {T in unionall_containers} = blahblah

```

but neither of these is valid Julia.

I’m interested in both

1. Learning about any relevant features or design patterns that could help in situations like these.
2. Understanding why Julia doesn’t allow something like that last bit of code above. Is there a deep reason why what I’m asking for is impossible?

In researching this, I learned about the existence of `TypeNames`, but I don’t really know if I should try to use them here, considering for instance performance concerns. The manual says very little about them.  
[Stripping parameter from parametric types](https://discourse.julialang.org/t/stripping-parameter-from-parametric-types/8293) is relevant, but doesn’t really solve my case. The second section of [this bit of the manual](https://docs.julialang.org/en/latest/manual/methods/#Design-Patterns-with-Parametric-Methods-1) also seems relevant, but I simply don’t understand it.

---

<div class="post-metadata">

### Author: ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)
#### Post date: [June 23, 2020, 3:00pm UTC](https://discourse.julialang.org/t/deparametrising-types/41939/2 "2020-06-23T15:00:40Z")

</div>

Adapting this one:

> [@Get new type with different parameter](https://discourse.julialang.org/t/get-new-type-with-different-parameter/37253/5):
>
> Have a read through: [https://docs.julialang.org/en/v1/manual/methods/#Extracting-the-type-parameter-from-a-super-type-1](https://docs.julialang.org/en/v1/manual/methods/#Extracting-the-type-parameter-from-a-super-type-1) The thing is, that parameters might change place, or don’t exist (see the BitVector example). Thus, it is generally not a good idea to do this. If you still want to do it, you probably want a generated function: julia\> @generated function newT(::Type{T}, ::Type{TT}) where {T,TT} getfield(parentmodule(T), nameof(T)){TT} end newT (generic function with 2 me…

?

---

<div class="post-metadata">

### Author: ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)
#### Post date: [June 23, 2020, 3:34pm UTC](https://discourse.julialang.org/t/deparametrising-types/41939/3 "2020-06-23T15:34:36Z")

</div>

Thanks, that’s indeed useful. That would give me a more generic way of implementing the `deparametrize` function, something like

```julia
deparametrized_typeof(::T) where T = T.name.wrapper

```

or

```julia
deparametrized_typeof(::T) where T = getfield(parentmodule(T), nameof(T))

```

Two follow-up questions:

1. Would something like this come with performance/type stability gotchas?
2. This doesn’t allow me to do the second thing I wanted to do, the diagonal dispatch type thing that I psedo-coded as `merge_containers(x::T1, y::T2) where {deparametrize(T1) == deparametrize(T2) <: AbstractContainer}`. If this is impossible to achieve, I would be happy if someone could help me understand why. It seems to me like an entirely natural thing to request from the type system.

---

<div class="post-metadata">

### Author: ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)
#### Post date: [June 23, 2020, 6:47pm UTC](https://discourse.julialang.org/t/deparametrising-types/41939/4 "2020-06-23T18:47:38Z")

</div>

> [@mhauru](#):
>
> - Would something like this come with performance/type stability gotchas?

Here’s the version of this I have, which also handles arbitraryily deep `UnionAll`s like e.g. `basetype(Array{Complex{T},N} where {T,N})`:

```julia
basetype(t::DataType) = t.name.wrapper
basetype(t::UnionAll) = basetype(t.body)

```

The DataType version is inferred and completely optimized away at compile time, you can see e.g. the output is the same for both:

```julia
julia> @code_llvm (() -> basetype(Array{Float64,2})([1,2,3]))()
julia> @code_llvm (() -> Array([1,2,3]))()

```

The UnionAll one also is inferred for some simple cases like `basetype(Array{Float32})`, but not for anything more complex. I’d be curious if anyone has a way to fix that though.

> [@mhauru](#):
>
> the diagonal dispatch type thing that I psedo-coded as `merge_containers(x::T1, y::T2) where {deparametrize(T1) == deparametrize(T2) <: AbstractContainer}`

My intuition would be that the most Julian solution is to drop the requirement that `x` and `y` be the same concrete type and instead write your code generically, such that the constraint could just directly be:

```julia
merge_containers(x::AbstractContainer, y::AbstractContainer) 

```

If that doesn’t work, then I think you can do this with some of the trait packages.

---

<div class="post-metadata">

### Author: ![mhauru](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhauru/32/208880_2.png) [@mhauru](https://discourse.julialang.org/u/mhauru)
#### Post date: [June 24, 2020, 5:15pm UTC](https://discourse.julialang.org/t/deparametrising-types/41939/5 "2020-06-24T17:15:57Z")

</div>

Thanks for the `@code_llvm` stuff, that helps.

For `merge_containers(x::AbstractContainer, y::AbstractContainer)`, this is in fact what I’m doing at the moment. The reason I’m not happy with this is that one could accidentally call `merge_containers` for two different container types, and while this is a nonsense operation, any sort of duck typing might take a while to catch it, or it might even go through but just lead to nonsense behavior. So I would rather catch it early and in an informative way.

I should look into if I can get what I want with traits, it’s time for me to get familiar with them anyway.
