I noticed something strange about reflection on parameterized types.
Consider this simplified code:
julia> type X{T <: Number} x::T end
julia> f{T}(::X{T}) = 1
julia> XT = methods(f).defs.sig[1]
X{T}
julia> XT <: X
*false*
julia> X <: XT
true
julia> type Y{T} y::T end
julia> g{T}(::Y{T}) = 1
julia> YT = methods(g).defs.sig[1]
Y{T}
julia> YT <: Y
*true*
julia> Y <: YT
true
That's weird, why do X and Y behave differently for subtype? Shouldn't a
TypeVar-parameterized version of a type always be a subtype of the general,
unparameterized type? The possible instances of the TypeVar-parameterized
type are certainly a subset of the possible instances of the
unparameterized type.
Maybe this sheds some light on it:
julia> X
X{T<:Number} (constructor with 1 method)
julia> Y
Y{T} (constructor with 1 method)
The unparameterized types read back as if they were parameterized! You
can't tell the difference between the TypeVar that was used to declare the
type (as in X), and a TypeVar of a method that was used to parameterize the
type (as in XT). Those two TypeVars named T are not the same object. It
seems like the <: operator is being confused by this.
If X actually was parameterized with an upper bound of Number, and XT was
parameterized with an upper bound of Any, then XT <: X = false would make
sense. But of course the two TypeVar's named T actually have the same
range, since X won't accept a parameter that is not a subtype of Number.
It seems like the parameters field of DataType is being overloaded for two
different purposes, in one case to remember the parameters that were
declared for a generic datatype, in the other case to remember the
parameters with which a generic datatype was instantiated. Shouldn't those
two purposes be separated?
Either I am confused and this is working the way it is supposed to, or it
is broken. Should I file a bug?