Indeed f
and g
above are equivalent as Michael noted. You can dynamically check it by writing:
julia> f(x::Vector{<:Number}) = sum(x)
f (generic function with 1 method)
julia> f(x::Vector{T}) where T<:Number = sum(x)
f (generic function with 1 method)
julia> methods(f)
# 1 method for generic function "f":
[1] f(x::Array{T,1}) where T<:Number in Main at REPL[2]:1
And Julia tells you that the method definition was overwritten (so that it was deemed to be equivalent).
However, in general scoping does matter. See this example:
julia> f(::Vector{Vector{<:Real}}) = "inner"
f (generic function with 1 method)
julia> f(::Vector{Vector{T}}) where {T<:Real}= "outer"
f (generic function with 2 methods)
julia> f(::Any) = "all else"
f (generic function with 3 methods)
julia> methods(f)
# 3 methods for generic function "f":
[1] f(::Array{Array{#s1,1} where #s1<:Real,1}) in Main at REPL[1]:1
[2] f(::Array{Array{T,1},1}) where T<:Real in Main at REPL[2]:1
[3] f(::Any) in Main at REPL[3]:1
julia> f([[1]])
"outer"
julia> f(Vector{<:Real}[[1]])
"inner"
julia> f([Any[1]])
"all else"
With regards to the performance in normal (i.e. type stable) code Julia performs static method dispatch so this should not matter. Probably you could design a contrived example requiring a complex dynamic method dispatch or dynamic code generation where this might matter (but for practical reasons this should not be relevant - just use the specification that represents your needs).