Difference between `f(x::Vector{T<:Integer})` and `f(x::Vector{T}) where T<:Integer`


I’m in the middle of reading the manual top-to-bottom and I can’t grasp the difference between:

f(xs::Vector{<:Integer}) = “foo”


f(xs::Vector{T}) where T<:Integer = “bar”

Can anybody help me out? Are they basically the same thing? I’m guessing yes, because after those two definitions, julia says there is only one method for that function:

julia> f
f (generic function with 1 method)

If that’s the case, why those two forms are supported, not just one?

You can ask julia for the answer:

julia> Tuple{typeof(f), Vector{<:Integer}} == Tuple{typeof(f), Vector{T}} where T <: Integer

In particular this is true for two reasons:
Vector{<:Integer} is syntactic sugar for Vector{T} where T<: Integer, so the tuple of the first type declaration is:

Tuple{typeof(f), Vector{T} where T<:Integer}

Now, that’s equivalent to putting the where on the outside because Tuple is covariant (unlike other types in the system). It wouldn’t be true for e.g. Ref:

julia> Ref{T where T <: Integer} == Ref{T} where T<:Integer

julia> Tuple{T where T <: Integer} == Tuple{T} where T<:Integer

With f(xs::Vector{T}) where T<:Integer = "bar" the type T can be used in the body of the function, the syntax f(xs::Vector{<:Integer}) = "bar" is shorter but doesn’t give you the possibility to use the type of the elements of the argument in the body of the function (however you may have means, in this case you can call eltype(xs)). If you don’t need the parameter, the shorter form is…well…shorter, when you need the parameter you may have no other choice than using the longer form.


That makes sense, thank you Keno and Mosè.