Getting the parametric constructors from `methods`

If I call methods(Dict), it just returns the “non-parametric constructors”:

julia> methods(Dict)
# 5 methods for type constructor:
[1] Dict() in Base at dict.jl:118
[2] Dict(ps::Pair{K, V}...) where {K, V} in Base at dict.jl:124
[3] Dict(kv::Tuple{}) in Base at dict.jl:119
[4] Dict(ps::Pair...) in Base at dict.jl:125
[5] Dict(kv) in Base at dict.jl:127

I can get the “parametric constructors” with a call like this:

julia> methods(Dict{Int,Int})
# 6 methods for type constructor:
[1] Dict{K, V}() where {K, V} in Base at dict.jl:88
[2] Dict{K, V}(d::Dict{K, V}) where {K, V} in Base at dict.jl:92
[3] Dict{K, V}(p::Pair) where {K, V} in Base at dict.jl:108
[4] Dict{K, V}(ps::Pair...) where {K, V} in Base at dict.jl:109
[5] Dict{K, V}(kv) where {K, V} in Base at dict.jl:100
[6] Dict{K, V}(slots, keys, vals, ndel, count, age, idxfloor, maxprobe) where {K, V} in Base at dict.jl:96

However, I had hoped to be able to use a call like methods(Dict{K,V} where {K,V}), but that just returns the “non-parametric constructors”. Is there a more generic way of getting the UnionAll constructor methods for a type, rather than calling methods on a specific instance like Dict{Int,Int}?

2 Likes

I suppose Dict is already Dict{K,V} where {K,V}, so it makes sense that those two return the same thing when provided to methods.

julia> Dict == (Dict{K,V} where {K,V})
true

I stumbled on a lifehack for parametric constructors! I was going to make a new topic, but maybe it’s fine to necropost when there’s no replies? Anyway, it seems to work on Dict (shows 11 methods), but I’ll demonstrate it on Ref because there’s an extra difficulty there:

julia> methods(Ref) # also methods(Ref{T} where T)
# 4 methods for type constructor:
 [1] Ref(x::Ptr{T}, i::Integer) where T
     @ Base refpointer.jl:142
 [2] Ref(x::Ref, i::Integer)
     @ Base refpointer.jl:141
 [3] Ref(x::AbstractArray, i::Integer)
     @ Base refpointer.jl:164
 [4] Ref(x)
     @ Base refpointer.jl:137

julia> methods(Ref{Int})
# 2 methods for type constructor:
 [1] Ref{T}() where T
     @ Base refpointer.jl:138
 [2] Ref{T}(x) where T
     @ Base refpointer.jl:139

However, the given parameters determine which methods the type reaches. In this case, a different parameter reaches more methods:

julia> methods(Ref{Ptr})
# 4 methods for type constructor:
 [1] Ref{P}(a::Array{<:Union{Cstring, Cwstring, Ptr}}) where P<:Union{Cstring, Cwstring, Ptr}
     @ Base refpointer.jl:145
 [2] Ref{P}(a::Array{T}) where {T, P<:Union{Cstring, Cwstring, Ptr}}
     @ Base refpointer.jl:148
 [3] Ref{T}() where T
     @ Base refpointer.jl:138
 [4] Ref{T}(x) where T
     @ Base refpointer.jl:139

But when we strip UnionAll to DataType by repetitively accessing the body field, we seem to get them all at once:

julia> methods(let x = Ref; while x isa UnionAll x = x.body end; x end)
# 8 methods for type constructor:
 [1] Ref{P}(a::Array{<:Union{Cstring, Cwstring, Ptr}}) where P<:Union{Cstring, Cwstring, Ptr}
     @ Base refpointer.jl:145
 [2] Ref{P}(a::Array{T}) where {T, P<:Union{Cstring, Cwstring, Ptr}}
     @ Base refpointer.jl:148
 [3] Ref(x::Ptr{T}, i::Integer) where T
     @ Base refpointer.jl:142
 [4] Ref(x::Ref, i::Integer)
     @ Base refpointer.jl:141
 [5] Ref(x::AbstractArray, i::Integer)
     @ Base refpointer.jl:164
 [6] Ref{T}() where T
     @ Base refpointer.jl:138
 [7] Ref(x)
     @ Base refpointer.jl:137
 [8] Ref{T}(x) where T
     @ Base refpointer.jl:139

I’m assuming this only works because the stripped DataType can actually be called for instantiation, which seems like a quirk of internals.

julia> Ref.body(6)
Base.RefValue{Int64}(6)

I’m stoked about this, but it’s still not proven if this gets ALL the methods; I don’t actually understand what methods’ docstring means by “the method table” when there appears to be overlapping ad hoc tables over the type parameters. This also doesn’t work for parametric function-like objects aka functors because their concrete types constrain the type parameters; there was work toward reflection with the callable’s type, but I do not know if it would handle unspecified type parameters. If there’s a more stable way, I’m all ears.

2 Likes