Destructuring Type Parameters

Is there any way to destructure type parameters when defining parametric types and methods.

Something along the lines of

struct Foo{U{T}} end

# Examples
Foo{Vector{Int}}()

Foo{AbstractArray{Int}}()

I know this particular example is simple to resolve with

struct Foo{U, T} end

but I have a more complicated situation in mind where the constraints on the types create redundancies in the parameterizations in a way where if I could destructure a type parameter I could infer another one

Could you clarify your goal/what problem are you trying to solve?

Say I want to create types for nodes that will exist in a DAG. Each node contains a val that will be updated on each iteration of an event loop. The type of val is the OutputType of the node.

One can use meta nodes which can provide insight into other nodes. For example you could have a node A with an OutputType of Float64. You could also have a meta node that stores the maxiumim of another node. The Max node’s OutputType would also be Float64 and it would be parameterized by the type of A as well. (The type of every node in the DAG is unique as it fully dictates the behavior of the node)

For example this can be achieved by the code below:

abstract type Node{OutputType} end

struct Foo{OutputType} <: Node{OutputType}
    val :: OutputType
end

struct Max{OutputType, NodeType <: Node{OutputType}} <: Node{OutputType}
    val :: OutputType
end

# Example
Max{Float64, Foo{Float64}}(1e-20)

However the definition of Max is a little redundant. The OutputType is fully determined by the NodeType but yet the programmer has to specify it. See above how Float64 is used twice in the construction of the example.

It’s just that when you write a type with the built-in curly-braces syntax, you have to be explicit. Max{Float64, Foo{Int}} is perfectly legal syntax, and it only errors because the OutputType doesn’t match in the specific Max definition. It makes no sense to allow Max2{Float32, Foo2} syntax as a shorthand for all definitions.

The other part of the issue is that because you didn’t use NodeType to annotate any fields, there wasn’t a default Max constructor method. It makes sense, Max(val) alone cannot tell you the entire NodeType. However, you can make a constructor method that accepts the needed type information. Here’s a quick one, though I bet it could be improved: Max(val, T::UnionAll) = Max{typeof(val), T{typeof(val)} }(val). You then just write Max(1e-20, Foo).

2 Likes