Difference in dispatching on Union type vs. subtype of Union type

Hello, I would like to dispatch on the Union type, if I explicitly write it down, it works:

julia> T = Union{<:Number, Ref{<:Number}}
Union{Ref{#s13} where #s13<:Number, #s12} where #s12<:Number

julia> f(x::T, y::T) = "ok"
f (generic function with 1 method)

julia> f(1, Ref(1))
"ok"

However, if I use the subtypeing gramma:

julia> g(x::U, y::U) where {U <: T} = "ok"
g (generic function with 1 method)

julia> g(1, Ref(1))
ERROR: MethodError: no method matching g(::Int64, ::Base.RefValue{Int64})
Closest candidates are:
  g(::U<:(Union{Ref{#s13} where #s13<:Number, #s12} where #s12<:Number), ::U<:(Union{Ref{#s13} where #s13<:Number, #s12} where #s12<:Number)) where U<:(Union{Ref{#s13} where #s13<:Number, #s12} where #s12<:Number) at REPL[8]:1
Stacktrace:
 [1] top-level scope at none:0

I thought both arguments should fit in the type:

julia> Ref(1)::T
Base.RefValue{Int64}(1)

julia> 1::T
1

julia> T <: T
true

What am I missing here? Thank you!

Ref is not a subtype of Union.

julia>  T = Union{<:Number, Ref{<:Number}}
Union{Ref{#s1} where #s1<:Number, #s2} where #s2<:Number

julia> Ref <: T
false

EDIT: Not the reason, I misread. Ok, it kinda is, but there’s more to it, see below.

1 Like

But what I am passing is a Ref{Int}, which should be a subtype of my Union according to the covariance:

julia> T = Union{<:Number, Ref{<:Number}}
Union{Ref{#s13} where #s13<:Number, #s12} where #s12<:Number

julia> typeof(Ref(1)) <: T
true

By writing

g(x::U, y::U)

you are making an assertion that x and y are of the same type (U).

4 Likes

This is the diagonal rule (should be mentioned in the manual):

f(x::T, y::T) where T = 1

only ever works if typeof(x)==typeof(y). Only after this check a possible subtype check on T is done. Example:

julia> f(x::T, y::T) where T<:Integer = 1                                                                                                                               
f (generic function with 1 method)                                                                                                                                      

julia> f(5,6)                                                                                                                                                           
1                                                                                                                                                                       
                                                                                                                                                                        
julia> f(5,UInt(6))                                                                                                                                                     
ERROR: MethodError: no method matching f(::Int64, ::UInt64)                                                                                                             
Closest candidates are:
  f(::T<:Integer, ::T<:Integer) where T<:Integer at REPL[1]:1                                                                                                           
Stacktrace:                                                                                                                                                             
 [1] top-level scope at none:0                                                                                                                                          
4 Likes

Indeed:

julia> g(x::H, y::G) where {G <: T, H <: T} = "ok"
g (generic function with 1 methods)

julia> g(1, Ref(1))
"ok"
1 Like

Thanks everyone. I just find something in the doc about the diagonal rule.