Several layers of keyword arguments

Suppose that I have several functions where one calling another. The example below is arbitrary but in reality foo2, foo3, and bar need different keyword arguments. From a caller perspective, I can specify any keyword arguments and all of them flow through the chain.

foo1(x; kwargs...) =  foo2(x; kwargs...)
foo2(x; kwargs...) =  foo3(x; kwargs...)
foo3(x; kwargs...) =  bar(x; kwargs...)

The problem is, the bar function (not owned by me) can only deal with its own recognized keyword arguments. Hence, it breaks when it sees additional ones that were only meant for foo2 and foo3.

How do I fix this? I can write a function that extracts what I need in foo2 and foo3 and reconstruct kwargs along the way. Is there a better way?

You could just write your own

bar_wrapper(args...; specific_keyword, kwargs...) = bar(args...; specific_keyword=specific_keyword)

And then call bar_wrapper from your code. I don’t see any reason that wouldn’t get fully inlined.

2 Likes

For call chains of medium complexity, I would wrap parameters in a NamedTuple, or a struct if things get more complex. Then everyone extracts what they need, and optionally you can provide an API with all the kwargs. Eg

foo_user(x; a = 1, b = 2) = foo1(x, (a = a, b = b))
foo1(x, params) =  foo2(x, @show params)
foo2(x, params) =  foo3(x, @show params)
foo3(x, params) =  bar(x, @show params)
bar(x, params) = params.a + x   # b ignored

then

julia> foo_user(1; a = 9, b = 3)
params = (a = 9, b = 3)
params = (a = 9, b = 3)
params = (a = 9, b = 3)
10

Nice! Just documenting the solution -

Problem:

julia> foo1(x; kwargs...) =  foo2(x; kwargs...)
julia> foo2(x; kwargs...) =  foo3(x; kwargs...)
julia> foo3(x; kwargs...) =  bar(x; kwargs...)
julia> bar(x; kwargs...) = begin
         valid_keys = (:a, :b, :c)
         !all(map(x -> x ∈ valid_keys, keys(kwargs))) && error("invalid keyword args")
         true
       end
julia> foo1(1, d=1)
ERROR: invalid keyword args

Fix:

julia> foo3(x; d=1, kwargs...) = bar(x; kwargs...)   # eats d
julia> foo1(1, d=1)
true