Clarification on function signature and dispatching behaviour in julia
Asked Answered
D

2

8

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}")

Danidania answered 14/1, 2020 at 4:48 Comment(2)
I can't reproduce your last definition of f() returning Int64,String,false. You might need to restart Julia if you add methods to functions. Just use a new character e.g. h for a new test. To your question: It appears that the system tries to find any solution to the type restriction, and T=Any, U=Any where U:<T is one. If you introduce a concrete type as in your third example it works as expected. People with a sounder Julia type system knowledge will soon give a proper answer to this.Prosaism
Oh! You're right, i needed to restart Julia... My redefinition with T<: Int did not override the last one with only T (without any warning...). And your answer about the system finding Any as a solution for T and U explains everything! Thank you :)Danidania
D
3

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"

  • 'T' is the "UnionAll" aka "Iterated Union" of all possible types of 'a'.
  • '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.

  • The same for U = Any since String <: Any.

So we now have U <: T that resolves to Any <: Any == true, and the method is called!

I hope I get it :)

Danidania answered 14/1, 2020 at 9:34 Comment(0)
M
4

What's happening here is that Tcan 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.

Microphone answered 14/1, 2020 at 7:45 Comment(1)
And why with the Tuple definition instead it rises the error ?Hidebound
D
3

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"

  • 'T' is the "UnionAll" aka "Iterated Union" of all possible types of 'a'.
  • '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.

  • The same for U = Any since String <: Any.

So we now have U <: T that resolves to Any <: Any == true, and the method is called!

I hope I get it :)

Danidania answered 14/1, 2020 at 9:34 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.