# Extracting type parameters from type with many parameters

I’ve got a type with six type parameters, something like this:

``````struct MyType{A <: S, a0, a1, B <: T, b0, b1} end
``````

My problem is that I want access to the type parameters in functions which take MyType values as arguments.
The best way of doing this that comes to mind is this:

``````my_a_type(::MyType{A, a0, a1, B, b0, b1}) where {A <: S, a0, a1, B <: T, b0, b1} = A
my_a0(::MyType{A, a0, a1, B, b0, b1}) where {A <: S, a0, a1, B <: T, b0, b1} = Val(a0)
my_a1(::MyType{A, a0, a1, B, b0, b1}) where {A <: S, a0, a1, B <: T, b0, b1} = Val(a1)
my_b_type(::MyType{A, a0, a1, B, b0, b1}) where {A <: S, a0, a1, B <: T, b0, b1} = B
my_b0(::MyType{A, a0, a1, B, b0, b1}) where {A <: S, a0, a1, B <: T, b0, b1} = Val(b0)
my_b1(::MyType{A, a0, a1, B, b0, b1}) where {A <: S, a0, a1, B <: T, b0, b1} = Val(b1)
``````

But this is repetitive, especially as the parameter names would be longer in real code. Is there a better way, perhaps?

if you need to do this it feels like they should be field instead of type parameter?

1 Like

Given a concrete type `Y`, a way you can get its parameter values is `Y.parameters`. Wouldn’t work on `MyType` itself because it’s an abstract type where the parameters aren’t specified, but it’d work on `MyType{Int, 1, :one, Float64, 3.5, 2}`. `.parameters` is not part of the public interface, so don’t count on it still being there in future versions of Julia.

The full struct has fields, right? It’s more typical to compute with those fields or use `typeof` if you really need a field’s type. It’s very rare to get parameters directly from a type, and when it happens, a specific parameter is accessed by an isolated method like `ndims` (very similar to `my_b_type`) rather than some generic `.parameters`-based method. The idea is that if you’re treating something like data to compute on, it should usually be structured as instances/fields instead of type-annotations/parameters.

1 Like

Oh, I get it. `A` and `B` I can get via `typeof` on some already-existing struct fields; and for `a0`, `a1`, `b0`, `b1` I just add some fields of type Val.

It’s not possible to say more on how you should organize the data without more context, but I would caution against that sort of information redundancy between fields and type parameters for `a0, a1, b0, b1`. It would mean storing the same type information in each and every instance, which can add up.

Is it at all possible that `a0, a1, b0, b1` could be refactored entirely as fields instead of parameters for the type? Generally information is put in parameters because it should be known at compile-time, such as for dispatching to different methods or compiling more performant code. The downside is that there are more types to compile each method for. 6 parameters is a LOT; assuming there are only 2 values for each parameter, that’s 2^6 = 64 types right off the bat. If your ratio of concrete types to instances over repeated runs in practice approaches 1:1 instead of 0:1, your runtime improvements could easily be swamped by compile-time latency and compiled code bloat. I know that it’s not always obvious how much information should be put in types, but an example of a 1:1 ratio is `struct CompileTimeInt{N} end`.

When you say “add up” you seem to be implying that adding Val fields would make my structs heavier, however I think this can’t be true, because each Val type is a singleton type, so instances shouldn’t consume space.

Example:

``````julia> sizeof((Val(7), Val(3)))
0
``````
1 Like

Oh that’s interesting, I never noticed that information in type parameters could trim a field’s memory usage to 0. I suppose the memory usage of instances with `Val` fields would scale with the number of unique `Val` types, not number of instances.

In the case of `(Val(7), Val(3))`, the `Tuple` type completely contains the information, so the instance doesn’t have to hold anything. But when a type, like an `Array`, can’t contain all the information, there is a memory cost; I don’t know the implementation detail exactly, but a `Val` array’s element looks pointer-sized.

Sizes of tuple or array before and after 1 additional Int8 element
``````julia> Base.summarysize( Int8.( (7,3) ) )
2
julia> Base.summarysize( Int8.( (7,3,1) ) )
3
julia> Base.summarysize( Val.(Int8.( (7,3) ) ) )
0
julia> Base.summarysize( Val.(Int8.( (7,3,1) ) ) )
0
julia> Base.summarysize( Int8.( [7,3] ) )
42
julia> Base.summarysize( Int8.( [7,3,1] ) )
43
julia> Base.summarysize( Val.(Int8.( [7,3] ) ) )
56
julia> Base.summarysize( Val.(Int8.( [7,3,1] ) ) )
64
``````