T is Int64, S is UInt8, but S is not a subtype of T. So, why would it dispatch? It seems that it’s enforcing S to be a subtype of Integer rather than a subtype of T.
No. There are infinite number of T that can match so T is not known at any time.
julia> foo(x::T, y::S) where {T <: Integer, S <: T} = (T, S)
foo (generic function with 1 method)
julia> foo(1, 0x00)
ERROR: UndefVarError: T not defined
Stacktrace:
[1] foo(::Int64, ::UInt8) at ./REPL[1]:1
[2] top-level scope at none:0
At the call site, I’m passing two arguments and they have type Int64 and UInt8 respectively. If I just match them to foo’s signature then T would be Int64 and S would be UInt8, except that it does not satisfy the where condition properly because Int64 <: Integer but UInt8 <: Int64 is not true.
I see that T is not defined in your example above but I don’t understand why it complains about that. Can you elaborate why there are infinite number of T that can match?
When I said dynamic, I was just referring to dynamic dispatch where at runtime the types of all arguments are known.
No T could be anything that is a supertype of Int64 and S could be anything that is a supertype of UInt8, there are infinite many of those.
It’s the same reason f(::T, ::Vector{T}) where T can be called with f(1, []). You can’t say in this case that you first match the ::T to T = Int since that’ll conflict with the ::Vector{T}. In this case T = Any in the end.
Because there are infinite number of types that is supertype of Int64.
I believe you are confused about the special rule about covariant type parameters. I don’t know what’s the exact definition of the rule right now (in corner cases, like when x and y have the same time shown in @giordano’s post ) but it says that if a type parameter only appears in covariant location then it must be a concrete type. This is so that (::T, ::T) where T won’t be the same as (::Any, ::Any) and (::Tuple{T,T}) where T won’t be the same as (::Tuple{Any,Any}). The rule does not apply since you are using the type parameter elsewhere in this case.
Dynamic dispatch means the dispatch is done at runtime, it’s in contrast to static dispatch and both are purely optimization concept and has nothing to do with the semantics. In fact, if not for all your calls are in the global scope, the dispatch is certainly going to be static.
The types of the argument of course must be know by dispatch time (which must be no latter than runtime) but that does not mean anything about type parameters. They don’t need to be assigned.