user2329125
user2329125

Reputation:

Julia fails to multiple dispatch

v06 I want to write a signature that expect 2 to 3 arguments. The first one is either an Integer or Vector of Integer. The second one is either a Vector of Integer or Matrix of Integer. The third one is either a Vector of Integer or not specified.

I first tried it like this

function foo(
a::Union{Integer, Vector{Integer}},
b::Union{Vector{Integer}, Matrix{Integer}},
c::Union{Void, Vector{Integer}} = nothing)

When I call this like this foo(3, [0o7, 0o5]) I get an error telling me that it is unable to match.

ERROR: LoadError: MethodError: no method matching foo(::Int64, ::Array{UInt8,1})
Closest candidates are:
  foo(::Union{Array{Integer,1}, Integer}, !Matched::Union{Array{Integer,1}, Array{Integer,2}}) at ...
  foo(::Union{Array{Integer,1}, Integer}, !Matched::Union{Array{Integer,1}, Array{Integer,2}}, !Matched::Union{Array{Integer,1}, Void}) at ...

Now I understand why julia is unable to match this Array{UInt8} <: Array{Integer} == false, but this just seems to be not smart of julia.

Then I tried this

foo(a::Union{Z1, Vector{Z1}},
    b::Union{Vector{Z2}, Matrix{Z2}},
    c::Union{Void, Vector{Z3}} = nothing
    ) where {Z1 <: Integer, Z2 <: Integer, Z3 <: Integer}

Now julia does not even tell me what does not match!

ERROR: LoadError: MethodError: no method matching foo(::Int64, ::Array{UInt8,1}, ::Void)
Closest candidates are:
  foo(::Union{Array{Z1<:Integer,1}, Z1<:Integer}, ::Union{Array{Z2<:Integer,1}, Array{Z2<:Integer,2}}, ::Union{Array{Z3<:Integer,1}, Void}) where {Z1<:Integer, Z2<:Integer, Z3<:Integer} at ...
  foo(::Union{Array{Z1<:Integer,1}, Z1<:Integer}, ::Union{Array{Z2<:Integer,1}, Array{Z2<:Integer,2}}) where {Z1<:Integer, Z2<:Integer} at ...

Upvotes: 3

Views: 224

Answers (1)

mbauman
mbauman

Reputation: 31372

Yes, Array{UInt8} <: Array{Integer} == false. This is called "parametric invariance." Lots of other questions have discussed this topic.

The other problem you're running into, though, is that when you use a static function parameter — that is, f(…) where T — the T must match something since it's available for use in the body of the function. This causes trouble in Unions where T isn't available in every option. I believe there's an open issue about changing this behavior to allow matching Union elements that don't contain T, which would turn that binding into an undefined variable if you tried to access it.

The workaround right now is to use type vars that aren't static parameters of the function. E.g.,

   foo(a::Union{Integer, Vector{<:Integer}},
       b::Union{Vector{<:Integer}, Matrix{<:Integer}},
       c::Union{Void, Vector{<:Integer}} = nothing) = 1

Upvotes: 7

Related Questions