Another option is to create your own singleton type to indicate keyword arguments that haven’t been filled in, and then call a function which dispatches on that. This approach is used e.g. here as part of the implementation for mapreduce
.
julia> struct _EmptyArg end
begin local z,Z = _EmptyArg(),_EmptyArg
_f(a, b, (c, d, e), ::NTuple{3,Z}) = let x=(;c,d,e), ks=filter(k->x[k]!==z,keys(x)), vs=map(k->x[k],ks); f1(a, b; NamedTuple{ks}(vs)...) end
_f(a, b, ::NTuple{3,Z}, (f, g, h)) = let x=(;f,g,h), ks=filter(k->x[k]!==z,keys(x)), vs=map(k->x[k],ks); f2(a, b; NamedTuple{ks}(vs)...) end
f(a, b; c=z, d=z, e=z, f=z, g=z, h=z) = _f(a, b, (c, d, e), (f, g, h))
end
f1(a, b; c=1, d=2.0, e="hello") = "$a $b $c $d $e"
f2(a, b; f="bye", g=π, h=nothing) = "$a $b $f $g $h";
julia> f(false,true; c=1//1)
"false true 1//1 2.0 hello"
julia> f(true,false; f="hi")
"true false hi π nothing"