I’ve ran into a type membership question with Type{T}, which seems to be behaving counterintuitively.
According to the documentation, Type{T} is the singleton consisting of the type T itself. Thus, isa(Int, Type{T}) returns true. However, if we combine this with a tuple, the relationship breaks down, as isa((Int,), Tuple{Type{Int}}) returns false. What is the logic as to why this is happening?
Digging into it more, the implementation of isa seems to provide some explanation. If the lhs of an isa call is a Type and the RHS is a DataType that is not superficially a Type{T} then it will always return false. This explains what’s happening in this particular case, but it’s not clear to me why this is correct.
Hmm, that’s not exactly the semantics I’m interested in. What I’m looking for in (for example) Tuple{Type{Int}, Bool}} is the type that describes the set of tuples whose first element is the type Int and whose second element is an instance of a boolean. If I were to write this as the type Type{Tuple{Int, Bool}}, then that’s the singleton that contains the tuple type, rather than the type of tuples with determined internal values.
I would recommend thinking of Type{T} as sugar for dispatching on a type value T , not as part of the type hierarchy.
The problem is that I’m working on a model of dispatch (for static analysis) and need to be able to simulate dispatch over my own representation of the currently available methods. The logical way to do this would be to build method signature equivalent tuple types and then use isa and subtyping to filter and do specificity ranking. However, the behaviour of Type{T} makes this challenging, because a Type{T} inside of a tuple seems to behave differently than a Type{T} outside of said tuple.
The alternative is to break apart the arguments to methods and test them independently, which gets around this particular issue, but creates new challenges (e.g. with the diagonal rule) where I need to make sure that type variable bounds are propagated properly through the membership testing.
Ah, I solved my problem, though it’s not a general solution to Type{T} oddness. In my particular case, I realized that arg_type_tuple isn’t equivalent to typeof in the case of Type arguments, so I can just duplicate that functionality and get semantically equivalent dispatch.