Is it possible to implement the following function:
get_parameters(::Type{T}) where {T} = T.parameters
without relying on the internal field parameters? As far as I can tell, the answer seems to be no, since this approach keeps coming up as an answer to the question of how to get a type’s parameters, e. g.
That requires you to implement get_parameters for every new type. Which is fine if you only need to extract the parameters from a particular type you know about. But it isn’t quite the same as a general reflection API.
On the other hand, extracting parameters of arbitrary types you know nothing about is of much more limited utility, unless you are writing some kind of generic debugging/introspection tool.
The key is really that you need to know something about the type itself in order to do anything meaningful with its parameters. And at that point, you might as well dispatch directly on the type (since you know it!).
What do you want to do with the parameters?
A decent alternative can be to expose the meaning you want in the parameters of an abstract supertype. The classic example here is AbstractVector{T}. I can’t expect that the first parameter of all arrays will be its eltype (simple counter-example: BitArray{1}). But I can dispatch on ::AbstractVector{T} and always know that the T there is the eltype.
If your use-case is interactive, where performance is not too important, and if you are happy with the textual representation in typeof, but want to capture the parameters in a Vector, you could parse this string representation using something like
function get_parameters(x)
s = string(typeof(x))
params = []
bracket_counter = 0 # nested param level
prev_param_end = nothing
for (i, c) in enumerate(s)
if c == '{'
bracket_counter += 1
isnothing(prev_param_end) && (prev_param_end = i)
elseif c == '}'
bracket_counter -= 1
end
if c == ',' && bracket_counter == 1 || c == '}' && bracket_counter == 0
push!(params, eval(Meta.parse(s0[prev_param_end+1 : i-1])))
prev_param_end = i
end
end
return params
end
Note that this not particularly elegant, but it gets the job done (for certain use-cases, as explained at the start of the post).
julia> struct S{A, B, C} end
julia> s = S{UInt16, typeof(rand(2, 3)), 12}()
S{UInt16, Matrix{Float64}, 12}()
julia> get_parameters(s)
3-element Vector{Any}:
UInt16
Matrix{Float64} (alias for Array{Float64, 2})
12
I appreciate all the answers trying to address potential use cases, but this was really a question about the language itself, so I’d be happy with a simple “no”. (Which seems to be the case?)