Thank you for your solution, I can see how it works but as you suggest it is possibly not the best. I won’t accept it as a solution yet in case someone has a more elegant one we can both benefit from.
If the example that you supplied is close to your actual usecase, the following appraoch would work:
julia> function shout(word::String, exclaim::Int)
println(word * "!"^exclaim)
end
shout (generic function with 1 method)
julia> function shout(word::String, question::Real)
println(word * "?"^question)
end
shout (generic function with 2 methods)
julia> function shout(word::String; kwargs...)
return shout(word, last(only(kwargs)))
end
shout (generic function with 3 methods)
Note, that in the first two methods, the second argument is not a keyword argument, but positional and thus dispatch works as expected.
The last method can take any keyword argument and simply passes its value on to either of the two other methods.
EDIT: If, however, you have more than one keyword argument you would need an intermediate step, that passes the arguments on in the correct order.
EDIT2: Unfortunately, this still does not work, as the question-method needs an Integer to work, but then it is dispatched to the exlaim-method. It is generally bad practice to suggest via the method signature, that the method would work with any Real, when it actually requires an Integer.
But if the two types Real and Integer where not related, this way would work.
EDIT3: The easiest way to solve your problem (although maybe not how you intended) would simply be:
julia> function shout(word::String; exclaim=0, question=0)
println(word * "?"^question * "!"^exclaim)
end
shout (generic function with 3 methods)
Again, thank you for your solution, I think it is still rather complex as highlighted with edits one and two.
You have successfully dissected my MRE to a point where it doesn’t replicate my actual problem which is fun to see Unfortunately, my actual use case requires Int and Real.
Edit 3 is interesting, it would be a more pythonic/ditch multiple dispatch approach which would work too. I’d have to have an if statement though, so it’d be more like
function shout(word::String; exclaim::Union{Nothing, Int}=nothing, question::Union{Nothing, Int} =nothing )
if exclaim === nothing && question === nothing
error("Only one of exclaim or question can be specified")
elseif !isnothing(exclaim)
println(word * "!"^exclaim)
elseif !isnothing(question)
println(word * "?"^question)
else
error("One of exclaim or question must be specified")
end
end
Although this would be its most verbose form, if after refactoring it would lose the beauty of multiple-dispatch.
Thank you for linking this package, I wasn’t aware of it. It should be perfect but I cannot get it to work as desired, I get the same error as in the initial example
julia> @kwdispatch shout()
julia> function shout(word::String; exclaim::Int)
println(word * "!"^exclaim)
end
shout (generic function with 2 methods)
julia> function shout(word::String; question::Real)
println(word * "?"^question)
end
shout (generic function with 2 methods)
julia> shout("Hello", exclaim=3)
ERROR: UndefKeywordError: keyword argument question not assigned
Stacktrace:
[1] top-level scope
@ REPL[34]:1
julia> shout("Hello", question=3)
Hello???
I have also tried @kwdispatch shout(word::String) in case the common argument is required. In the examples given they only seem to have kwargs, so I’m not sure if there is a problem having positional and keyword?
using KeywordDispatch
@kwdispatch shout(word::String)
@kwmethod function shout(word::String; exclaim::Int)
println(word * "!"^exclaim)
end
@kwmethod function shout(word::String; question::Real)
println(word * "?"^question)
end
shout("Hello", exclaim=3)
shout("Hello", question=3)
I guess, in principle, EDIT2 would be the right way for your requirement. The only problem is, that exclaim method takes an Int and the question-method a Real (i.e. the supertype of both Integer and AbstractFloat). This means, that via normal dispatch you’re only able to reach the question-method if the argument is not <:Integer (which probably is the reason, that you are reaching for keyword arguments in the first place).
That said, one way to get around this is with value types, that allow you to dispatch on values, rather than types:
julia> function shout(word::String; kwargs...)
key,value = only(kwargs)
shout(word, value, Val{key}())
end
shout (generic function with 1 method)
julia> function shout(word::String, question::Real, ::Val{:question})
println(word * "?"^question)
end
shout (generic function with 2 methods)
julia> function shout(word::String, exclaim::Int, ::Val{:exclaim})
println(word * "!"^exclaim)
end
shout (generic function with 3 methods)
I have this working now, when including the positional arg in @kwdispatch I was forgetting to use @kwmethod. It is unfortunate that KeywordDispatchcannot have default arguments though as my real use case would benefit from this. But that wasn’t the original question, thank you for your solution
Thank you for all your solutions, I need to think about what suits my application best as some of these options are less intuitive than regular multiple dispatch. For code clarity it might be best to have two distinct functions.