Best practices to add more than one bound to type parameters

I tried to achieve the double constraint (NTuple{N, T} and TwoOrThreeTuple{T}) by defining an intermediate abstract type alias. However, I encountered inconsistent result from Julia’s compiler:

julia> abstract type MyType{V} <: Any end

julia> const TwoOrThreeTuple{T} = Union{NTuple{2, T}, NTuple{3, T}}
Union{Tuple{T, T}, Tuple{T, T, T}} where T

julia> const MyTwoThree{T, V<:TwoOrThreeTuple{T}} = MyType{V}
MyTwoThree{T} where T (alias for MyType{V} where {T, V<:Union{Tuple{T, T}, Tuple{T, T, T}}})

julia> struct myT2{T, N, V<:NTuple{N, T}} <: MyTwoThree{T, V}
           a::V
       end

julia> myT2((1,))
myT2{Int64, 1, Tuple{Int64}}((1,))

julia> MyTwoThree{Int, Tuple{Int}}
ERROR: TypeError: in MyType, in V, expected V<:Union{Tuple{Int64, Int64}, Tuple{Int64, Int64, Int64}}, got Type{Tuple{Int64}}
Stacktrace:
 [1] top-level scope
   @ REPL[6]:1

As you can see, MyTwoThree should forbid any types that are not subtypes of TwoOrThreeTuple from being its second parameter (V). In fact, if you try to write such an illegal instance of MyTwoThree, it can correctly detect the issue. However, when I tried to construct a myT2 with an illegal parameter V::Tuple{Int}, the construction bypassed this check.

I’m unsure whether this is considered a “bug”, or another “feature” of Julia’s type system. I am concerned either way.

1 Like