Given a defined composite type with multiple constrained type parameters, e.g.:
julia> struct myT{T1<:Real, T2<:Integer} end
When we reference its type signature elsewhere (e.g., for a method definition), it’s common to specify only one (or part) of the parameters. When the one(s) to be specified (constrained) is not the first, we often resort to <:Any
as a placeholder for the unspecified parameter(s):
julia> foo(arg::myT{<:Any, Int}) = (arg,)
foo (generic function with 1 method)
However, in this case, myT{<:Any, Int}
ends up being a UnionAll
that is no longer a subtype of the original type myT
which is practically sufficient to bound all the concrete type instances of myT
.
julia> (myT{<:Any, Int}) == (myT{T1, Int} where {T1})
true
julia> myT{<:Any, Int} <: myT
false
IMHO, not only is it conceptually jarring because UnonAll
is supposed to represent all possible (hence legal) types tied to the original parametric type myT
, but it may create confusion for the user of the parametric type who did not define myT
in the first place. Even if they are the owner (i.e., they defined myT
), they would have to code the full type signature everywhere else manually:
julia> foo2(arg::myT{<:Real, Int}) = (arg,)
I think a potential remedy for this “issue” is to provide a separate syntax :<:
(or any other preferred symbol) such that:
myT{:<:, :<:} == myT{<:Real, <:Integer} == myT
This solution is similar to an open issue on GitHub, where a syntax sugar is proposed to conveniently ignore some type parameters. The subtle difference is that the original bound of the parameter is always preserved by :<:
in this case.