Reputation:
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
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 Union
s 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