Function signature doesn't match when using Union


#1

Using julia 0.6.3-pre.2 (built from git), I tried the following:

julia> foo_bar{T<:Union{AbstractString, Symbol}}(d::Union{Symbol, AbstractVector{T}}=AbstractString[]) = "OK"
foo_bar (generic function with 2 methods)

julia> foo_bar()
"OK"

julia> foo_bar([:abc])
"OK"

julia> foo_bar(["abc"])
"OK"

julia> foo_bar(Union{AbstractString, Symbol}["abc", :abc])
"OK"

julia> foo_bar(:abc)
ERROR: MethodError: no method matching foo_bar(::Symbol)
Closest candidates are:
  foo_bar(::Union{AbstractArray{T<:Union{AbstractString, Symbol},1}, Symbol}) where T<:Union{AbstractString, Symbol} at REPL[1]:1
  foo_bar() at REPL[1]:1

julia> 

So my question is why doesn’t Symbol match Union{AbstractArray{T<:Union{AbstractString, Symbol},1}, Symbol}?

I’ve also noticed that if I define my function like this:

foo_bar{T<:Union{AbstractString, Symbol}}(d::Union{T, AbstractVector{T}}=AbstractString[]) = "OK"

Then it correctly matches, but then it also matches foo_bar("abc")

I know the best option would be to define different function signatures, but in the code that I’m using, this is a keyword argument, and for backwards compatibility with our legacy code, we need to support the old Symbol method.

Thanks,

Philip


#2

The problem is that when you pass in just a Symbol argument, the type-variable T is not defined. Julia v0.6 won’t let you do that, because it requires that all type-variables must have defined values in order for a method to match.

Julia v0.7 removes that requirement, so your example will just work in the future:

julia> foo_bar(d::Union{Symbol, AbstractVector{T}}=AbstractString[]) where {T <: Union{AbstractString, Symbol}} = "OK"
foo_bar (generic function with 2 methods)

julia> foo_bar(:a)
"OK"

(as long as you don’t actually refer to T inside the body of your function, since T is still not defined when you pass in a Symbol).

If you don’t actually need T at all inside the function, then you can make this work on v0.6 and v0.7 by using the AbstractVector{<:Foo} construction instead:

julia> foo_bar(d::Union{Symbol, AbstractVector{<:Union{AbstractString, Symbol}}}=AbstractString[]) = "OK"
foo_bar (generic function with 2 methods)

julia> foo_bar(:a)
"OK"

#3
julia> foo_bar(d::Union{Symbol, AbstractVector{<:Union{AbstractString, Symbol}}}=AbstractString[]) = "OK"
foo_bar (generic function with 2 methods)

julia> foo_bar()
"OK"

julia> foo_bar([:abc])
"OK"

julia> foo_bar(["abc"])
"OK"

julia> foo_bar(:abc)
"OK"

Edit: Sniped.


#4

Thanks, makes sense and works, though not in 0.4, and I need to figure that out still (legacy code and all that)