I’m working on a very reflective program, and need to take a type with specified type parameters (e.g. Array{Int64, 1}) and turn it back into a UnionAll (e.g. Array{T, N} where T where N), without mutating the source DataType, ideally without having to eval a synthetic expression. This seems like something that should be possible, but the trivial ideas don’t seem to work:

There doesn’t appear to be a built in function to do this, though I could just be missing it.

There’s no DataType constructor that lets one modify the new type’s properties.

DataType.parameters is (for most types) an svec, which can’t be mutated from Julia.

clone isn’t defined for types, and Base.deepcopy returns the same vector, so replacing the parameters field with a fresh svec won’t work.

@rdeits In my use case, I’m interested in getting a new type that doesn’t have the parameters of the old one, not modifying the type that already exists. @yuyichao’s answer is ideal.

In this particular case, I needed this in order to check to see if there was an extant method implementation for a specific argument type in a typechecker, in a kind of ghetto re-implementation of ml_matches with nonzero lim, as a result of wanting to precisely identify methods potentially invoked by a call (methods conservatively over-approximates the set, in the case where all subtypes of a type have implementations) and check to make sure that implementations exist for every subtype. Since I’d rather implement as much of my algorithm in Julia as possible, I would at this point rather avoid calling ml_matches.

The problem that required this arises when filtering the list of potentially invoked methods by type. My algorithm works by subsetting the list of methods to those in a typing relation to the current “objective” type, then recursing on the subtypes of the objective until it either runs out of methods (a failure, in which case a more general implementation must exist/be used), or run out of subtypes (a success). This runs into trouble, however, in the case of something like trying to find an implementation of size on an AbstractSparseArray{Int64,Ti,1} where Ti, whose sole subtype (in a fresh 0.6.0 REPL) is SparseVector{Int64,Ti} where Ti<:Integer. However, only one implementation of size is available, taking a SparseVector{T,V} where T where V, which is not a subtype or supertype of AbstractSparseArray{Int64,Ti,1}, which is removed. Then, the next step of the algorithm tries to find a method for SparseVector{Int64, Ti} where Ti <: Integer. However, since the implementation for SparseVector{T,V} where T where V was filtered out at the previous iteration, this fails.

The fix is that instead of filtering on subtypes of AbstractSparseArray{Int64,Ti,1}, the list of functions is filtered based on subtypes of AbstractSparseArray{T,U,V} where T where U where V, of which SparseArray{T,U} where T where U is a subtype. This works perfectly for my limited use case. At some point I’ll rip this out and replace it with a C call to ml_matches with the appropriate limited mode turned on and some patches to require exhaustiveness, but I wanted to see if I could write it all in Julia.