Edit: @lmiq beat me to it by one minute
The distinction is that the first method requires that the two characters have the same type (and that that must be some subtype of Character
. Here’s a simpler example:
julia> function f(x::T, y::T) where {T <: Integer}
x + y
end
f (generic function with 1 method)
julia> f(Int32(1), Int64(1))
ERROR: MethodError: no method matching f(::Int32, ::Int64)
Closest candidates are:
f(::T, ::T) where T<:Integer at REPL[6]:1
Stacktrace:
[1] top-level scope
@ REPL[8]:1
julia> function g(x::Integer, y::Integer)
x + y
end
g (generic function with 1 method)
julia> g(Int32(1), Int64(1))
2
A further difference is that having an explicit type parameter lets you express more complicated relationships, such as:
julia> function h(x::T, y::Matrix{T}) where {T}
x * y
end
h (generic function with 1 method)
julia> h(2.0, ones(Float64, 2, 2))
2×2 Matrix{Float64}:
2.0 2.0
2.0 2.0
here h
is a function that will take any x
and a y
whose scalar type is the same as the type of x
, whatever that type may be. That’s not a relationship you can express via dispatch without type variables.