I was trying out some things in the Julia (1.2) REPL and I stuck my mind on something I don't understand about dispatching.
I first tried this thing which is working the way I expected:
f(a::T) where {T <: Int} = "Test"
Calling f(3) works since Int <: Int == true
Calling f("Hello") results in an "MethodError: no method matching" error since String <: Int == false
Then, I tried this method, and I don't understand why calling it works in some case:
f(a::T, b::U) where {T, U <: T} = "Another Test"
Calling f(3, 3) works (as I expected)
BUT f(3, "Hello") also works and does not throw a "MethodError: no method matching" ???
I thought that (since T becomes an Int and U a String) String <: Int == false
???
I guess I am missing something pretty straightforward here but I can't find it... So this is my question, why f(3, "Hello") is working???
Moreover, I tried this snippet of code (i tried to recreate the second method signature) and it correctly fails as I expected:
Test = Tuple{T, U} where {T, U <: T}
Test{Int, String}
(this fails as i expected with "TypeError: in Type, in U, expected U<:Int64, got Type{String}")
What's happening here is that T
can be a datatype and U
can be any supertype of string. This the conditions are fulfilled. The thing that was tripping you up with string not being a subtype of int is a red herring since no concrete type is the subtype of any other.
Ok, thanks to laborg it seems I now understand what was going on. If we take this method:
f(a::T, b::U) where {T, U <: T} = "Another Test"
'U' is the "UnionAll" aka "Iterated Union" of all possible types of 'b' that are subtypes of 'T'
What I was misunderstanding was the fact that if (example) a::Int, then T can take a parent abstract type of typeof(a). Since Int <: Any, then T = Any is a valid solution for dispatch.
So we now have U <: T that resolves to Any <: Any == true, and the method is called!
I hope I get it :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With