Adding constrain on the range of `N` in parametric types for function arguments

I’d like to know if it’s possible to set a range of N in a parametric type PT{T, N} when it’s the type of an argument for a defined function.

E.g.

f(a::Array{<:Number, N}) where {N} = a

and I want to set some constrain on N such as N < 4.

I know I can explicitly write down something like

f(a::Union{Array{<:Number, 1}, Array{<:Number, 2}, Array{<:Number, 3}}) where {N} = a

but I’d like to know if there’s some more efficient way. Thank you!

How about

Union{(Array{<:Number, i} for i in 1:4)...}
1 Like

AFAIK the answer is no. Basically, despite N looks like a number, it’s really just a single type no different than Int64 or Float64. Think of it as Val{N}. Because of this, compiler doesn’t do “math” on types, only <: and :>, which is why the answer is probably no.

1 Like

Yeah, I think this is as close as we can get here. Thank you!

Yeah. I get that N is still treated as another type in the type system so that we can only do type operations such as <: and :> here. I wish we could treat it as a primitive-type variable but maybe that will break the function initialization during the compile time? I’m not sure if it’s a design choice or some feature that can be added in the future.

If this is for dispatch, I would go with a trait, eg a variant of

in_range(::Array{T,N}) where {T<:Number,N} = Val(N < 4)

_f(::Val{true}, a) = "in range"

f(a) = _f(in_range(a), a)

(note that it is good style to allow <:AbstractArray in most Julia code).

If this is for validation, consider a wrapper type that checks N in its inner constructor.

2 Likes

Yeah, I meant to use it for multiple dispatches. This also seems like a good workaround. Thanks!