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:
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:
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,
deparametrize(::Type{<:ContainerType1}) = ContainerType1
after which I can do
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
:
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
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
merge_containers(x::T1, y::T2) where {deparametrize(T1) == deparametrize(T2) <: AbstractContainer} = blahblah
or maybe
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
- Learning about any relevant features or design patterns that could help in situations like these.
- 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 is relevant, but doesn’t really solve my case. The second section of this bit of the manual also seems relevant, but I simply don’t understand it.