Wrong dispatch due to kwargs?

It seems to me that the dispatch system is doing something wrong. Apologies ahead of time if there’s something blindingly obvious that I’m missing here.

See the examples below:

This is as expected:

function func(
    x::Vector{Char};
    convert::Bool=true,
)
    println("x=$x")
end

x = "julia" |> collect  # typeof Vector{Char}
func(x)      # this line prints: x=['j', 'u', 'l', 'i', 'a']

But what’s going on here?!

function func(
    x::Vector{Char};
    convert::Bool=true,
    other::Vector{String}=[],
)
    println("x=$x")
end

x = "julia" |> collect
func(x)
# ERROR: MethodError: no method matching var"#func#44"(::Bool, ::Vector{Any}, ::typeof(func), ::Vector{Char})
# Closest candidates are:
#   var"#func#44"(::Bool, ::Vector{String}, ::typeof(func), ::Vector{Char}) at ~/repos/test_arrow_compression.jl:30

It tells me no method matching func(::Bool,... which I’m not trying to call and suggests that the closest candidate is func(::Bool... which doesn’t exist. I suspect this Bool comes from the convert kwarg.

In the process of building this minimal example I actually found a way of making it work:

function func(
    x::Vector{Char};
    convert::Bool=true,
    other::Vector{String}=String[],
)
    println("x=$x")
end

x = "julia" |> collect
func(x)  # this line prints: x=['j', 'u', 'l', 'i', 'a']

If you’ll make experiments with this, make sure you clear the function each time so that the experiments are independent:

for m in Base.methods(func)
    Base.delete_method(m)
end

I’ve checked that this happens with both 1.8.5 and 1.9.0-rc1:

julia> versioninfo()
Julia Version 1.8.5
Commit 17cfb8e65ea (2023-01-08 06:45 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 8 × Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, skylake)
  Threads: 1 on 8 virtual cores
Environment:
  JULIA_EDITOR = code
  JULIA_NUM_THREADS = 

The error message is misleading, and someone more qualified to comment on what can be done should chime in there.
But this is happening because you’re naming a keyword argument convert, which is the built-in function required for turning an Any[] into a String[]. So when you type it correctly and don’t need the convert operation, the error magically goes away.
^ this was incorrect. See correct answer below.

3 Likes

I’m not sure I understood. Even giving it another name, it still fails the same way:

function func(
    x::Vector{Char};
    convert_::Bool=true,
    other::Vector{String}=[],
)
    println("x=$x")
end

Your error is because you have typed other as a Vector{String} but have provided a default value [] whose type is Vector{Any}.

(Since no type information is given, [] must be typed as a container to accept values of any type. You can check this as typeof([]) == Vector{Any}.)

The multiple dispatch is then failing because when you don’t provide an explicit value for other, you are in fact calling it as f(x::Vector{Car}, true::Bool, []::Vector{Any}), which isn’t defined.

This will work if you provide the default value as String[].

6 Likes

The error message says something completely different.

MethodError: no method matching var"#func#44"(::Bool, ::Vector{Any}, ::typeof(func), ::Vector{Char})

Just a reminder: in Julia (multiple) dispatch operates only on positional arguments, not on keyword based ones.

1 Like

Yeah it looks like that type signature is the result of using the keyword arguments. It’s created a number of wrapped function depending on your arguments which I’ll admit makes the error message confusing.

It’s simpler if we provide default values to positional arguments - we can generate the same error and the type signature is more straightforward:

func(x::Vector{Char}, convert_::Bool=true,  other::Vector{String}=[]) = println("x=$x")

func(Char['h', 'i']) # LoadError: MethodError: no method matching func(::Vector{Char}, ::Bool, ::Vector{Any})

(Note I’ve removed the semicolon from the argument list.)

The same thing is going on in the case where you’ve used keyword arguments: ultimately your calling types don’t match your declared types.

2 Likes