Firstly, note that the divide is between keyword arguments and positional arguments. Keyword arguments can be made mandatory (by not giving them a default value) or optional, and the examples in your question show optional keyword arguments and optional positional arguments.
As for the difference,
- In terms of interface, optional positional arguments are generally values that are part of the core operation of the function, that happen to have sane default values. They are things that you would expect to be part of the function signature. Keyword arguments are more likely to be used for tweaks and enhancements to the core task of the function, that are useful only in specific scenarios. This is a general guideline with a lot of exceptions though.
- In terms of the language and performance, keyword arguments don’t participate in multiple dispatch. So, you can’t specialize on their type, and cannot create different methods based on them.
julia> fun(positional::Int) = 42
fun (generic function with 1 method)
julia> fun(positional::Float64) = 42.0
fun (generic function with 2 methods)
With a positional argument (whether it’s optional or not), specializing for a different type (Int
and Float64
above) creates a new method, so fun
above has 2 methods now. (This is the core of Julia’s multiple dispatch and allows very efficient compiled code to be generated, so can matter a lot for performance.)
julia> func(; keyword::Int) = 42
func (generic function with 1 method)
julia> func(; keyword::Float64) = 42.0
func (generic function with 1 method)
With a keyword argument (again, whether optional or not), specifying a new type for the keyword argument just redefines the function, so we only have 1 method for func
in the end.