I have a function f(a; b=default_b, c=default_c), and want to implement a method f(a) = f(a, b=default_b, c=c_specialized_for_default_b), which should override f when neither b and c are provided. If I implement it like this, Julia does not choose the second method, but return Method definition overwritten (which is kinda expected, although we could hope Julia choose the latter one when calling f(a) by some dispatch-like reasoning).
I can implement it like this:
f(a; b=default_b, c=default_c)
if b == default_b && c == default_c
c = c_specialized_for_default_b
end
# some code
end
Is there a cleaner way (and maybe more efficient) to do this? Maybe there is a way to check if arguments are provided ?
julia> function f(a; b=nothing, c=nothing)
if isnothing(b) && isnothing(c)
return _f(a)
else
return _f(a,b,c)
end
end
f (generic function with 1 method)
where _f(...) is the internal function for which the one with keyword arguments is the exposed interface.
As you noted keyword arguments don’t take part in dispatch, so you need to use some other mechanism. The primary options I know are
Do it Python-style with f(a; b = nothing, c = nothing) and do isnothing checks to fill in desired defaults. This may have some type stability issues so it’s usually a good idea to have a function barrier before doing actual computations (i.e. call a new function or a different method of the same function).
Define your function as f(a; kwargs...) and introspect kwargs to decide how to proceed with the call.
For the second option your example could be
function _f(a; b = default_b, c = default_c)
# some code
end
f(a; kwargs...) = isempty(kwargs) ? _f(a; c = c_specialized_for_default_b) : _f(a; kwargs...)
Ok, thanks for your replies. So if I understand correctly, I need to change the definition of f(a::Type1; b::Type2=default_b, c::Type3=default_c) to f(a::Type1; b::Union{Nothing, Type2}=nothing, c::Union{Nothing, Type3}=nothing) (with the arguments management to call internal function that go with it) and there will be no breaking changes, am I correct ?
I think if your objective is just to change the default value of c when b has the default value then your original solution may be the best. Note that just because Julia does not dispatch on keyword arguments it does not mean it cannot specialize on them, if performance is what you are worried about.
My concern about performance was in the comparison on arguments to check if they are defaults, which could be costly if the objects are huge. Using nothing solves this, but this is not critical in my problem.