Fun with kwargs methods

Consider

julia> bar(x, y) = x / y
bar (generic function with 1 method)

julia> bar(x, y; c) = x + y + c
bar (generic function with 1 method)

julia> bar(3,4,c=5)
12

julia> bar(3,4)
ERROR: UndefKeywordError: keyword argument c not assigned
Stacktrace:
 [1] bar(::Int64, ::Int64) at ./REPL[2]:1
 [2] top-level scope at none:0

However (restarting Julia):

julia> bar(x, y; c) = x + y + c
bar (generic function with 1 method)

julia> bar(x, y) = x / y
bar (generic function with 1 method)

julia> bar(3,4)
0.75

julia> bar(3,4, c=5)
12

Is there a way to have the order not matter when creating two methods, one that requires a kwarg and one that doesn’t?

2 Likes

Keyword arguments affect methods dispatch · Issue #9498 · JuliaLang/julia · GitHub, it’s a method override.

1 Like

Thanks, @yuyichao. I hope this can be fixed soon.

Note that “fixing it” will likely NOT mean it’s order independent. The only issue here is the inconsistency between method overwrite and (non-)dispatch sensitivity of keyword argument.

The fix should be either make dispatch fully aware of keyword argument and make this a true partial overwrite (similar to what would happen if you define a method with optional argument) and keep the behavior you observe. Or the fix could be making this a full overwrite instead of a partial one leaving both the keyword and non-keyword method visible even though there are only supposed to be one method from the dispatch POV.

The order dependency you observe is caused by the overwrite, which is real and isn’t fixable.

I guess there could be a variance of making dispatch fully keyword aware that treat required keyword argument in a special way so that this won’t be an overwrite anymore. I’m not sure how easy it is to implement this behavior or making dispatch keyword aware though…

So has this problem any solution?
I want to define two methods (or 1 method if possible) as follows:

zerosM(dim::Integer) = zeros(dim, dim)
zerosM(dim::Integer; like::Array) = zeros(eltype(like), dim, dim)

How can I do this? like is optional.

zerosM(dim; like::AbstractArray=Vector{Float64}()) = zeros(eltype(like), dim, dim)

should do what you want.

3 Likes

Thank you! For this case that we know that the default eltype of zeros() is Float64, this works!

For other situations, we came up with this in Slack group which does the dispatch using if else:

zerosM(dim::Integer; like::Union{Array,Nothing}=nothing) = like === nothing ? zeros(dim,dim) : zeros(eltype(like), dim, dim)