How to constrain the type parameters for this function

given an example function

function foo(a,b)

a would accept vector of real numbers, such as Vector{Int64}, Vector{Float64}, ...
b should be Integer while elements of a are Integers, and Integer or Float while elements of a are Floats.

Here is a type constrained version

function foo(a::Vector{T},b::T) where {T<:Real}

The problem of this version is that the Integer is not a subtype of AbstractFloat, so that

foo([1,2,3], 5) is allowed, and foo([1.0,2.0,3.0], 5) is not allowed, but foo([1.0,2.0,3.0], 5.0) is ok.

How can i constrain the type parameters so that the desired behavior (last two invoke) be achieved in one method definition, or is it possible to do at all?

I think you want foo(a::Vector{T1},b::T2) where {T1 <: Real, T2 <: Real} (or foo(a::Vector{<:Real},b::Real) if you don’t need the type parameters).

The T2 should be Integer when T1 is Integer.

foo(a::Vector{<:Real},b::Real) will make
foo([1,2,3], 5.0) allowed, but is not the desired behavior

The function would be an algorithm for discrete and continues domains, so when a is represented in discrete integers, b should also be integer, on the other hand, if a is floating points, it doesn’t matter b in float or integer. The original type constrained version force b be the same floating point type.

For the cases you want to throw (e.g. vector of integers with floating point b), I would add a check that throws with a helpful error message, or define a method that this case hits to throw a helpful error message.

If the types are known at compile time (i.e. the call to foo is typestable), this would have 0 runtime overhead in the cases you support.

If you’d prefer a MethodError, and you’re also handling all the cases you do support with the same implementation, then you can make that implementation an unconstrained _foo, and have a foo for each case that you do support forward there.
I’d do what is simplest (so long as they have 0 runtime overhead, which is the case for all proposals here), which depends on what foo’s implementation looks like.

Like if you need different implementations on input types, it’d make sense to just define those directly, and not define the type combinations you don’t support.

1 Like

so far the implementation is the same, so i wandered if i can do this in one method definition. I’ll use runtime check for now.

Note that this “runtime check” will have 0 runtime cost, because the compiler will delete it when specializing on the input types.

The revised version follows:

function foo(a::Vector{T},b::T1) where {T<:Real,T1<:Real}
  if T <: Integer && !(T1 <: Integer)
    error("b should be Integer")