Smart kwargs... dispatch


#1

Let’s say a function f() involves another function with multiple arguments. From what I understood, one can avoid listing them with with the kwargs... argument that “passes” the optional arguments down to the other functions.

function f(x; kwargs...)
   g(x; kwargs...)
end

Is it possible to setup something similar but when two functions are involved, and smartly dispatch appropriate arguments to each function.

function f(x; kwargs...)
   y = g(x; kwargs...)
   z = h(y; kwargs...)
end

Currently, it would throw a MethodError as some of the kwargs of g() do not exist for h().


#2

This is a typical idiom in R, but I would avoid it in Julia. Instead, I would prefer

function f(x, args)
   y = g(x, args)
   z = h(y, args)
end

and make args a NamedTuple.

g and h can then pick the fields they like. Also see Parameters.@unpack.

When the interface stabilizes, you can collect the args in a struct, without changing f, g, or h fundamentally.


#3

I can’t say if it is advisable, but it is possible. All you need to do is to slurp the extra kwargs in g and h as well.

function f(x; kwargs...)
   y = g(x; kwargs...)
   z = h(y; kwargs...)
end

g(x; g_kwarg="hello", kwargs...) = <some function>
h(x; y_kwarg=:goodbye, kwargs...) = <some other function>

This way, you don’t get any method errors. On the other hand, it will no longer warn you if you pass a misspelt kwarg.


#4

The problem with this is that you cannot build in defaults into the args field, right? On the other hand @korsbo approach has pretty low overhead

julia> @noinline f1(;x::Int = 1, kwargs...) = x; @noinline f2(;y::Int = 2, kwargs...) = y;

julia> f(; kwargs...) =  f1(;kwargs...) + f2(;kwargs...)
f (generic function with 1 method)

julia> @btime f()
  0.026 ns (0 allocations: 0 bytes)
3

julia> @btime f(x=4)
  17.066 ns (1 allocation: 32 bytes)
6

#5

If you know in advance what are the keywords that f accepts you could also do something like:

function f(x; kwargs...)
   belongstog(s) = first(s) in v # where v are the keywords that f accepts
   kw1 = filter(belongstog, kwargs)
   kw2 = filter(!belongstog, kwargs)
   y = g(x; kw1...)
   z = h(y; kw2...)
end

#6

That’s reminiscent of Mathematica’s approach. But in Mathematica we have Options[function], which returns the kwargs in function. Does anybody know if there a way to obtain the kwargs admitted by a function (the v in @piever’s code) by introspection, somehow?


#7

I was wondering if a syntax like f( args; kwargs1... ;kwargs2... ; ... )
would make sense for this, then one could define

function f(; kwargs1... ; kwargs2... )
  g( ;kwargs1... )
  h( ;kwargs2... )
end

#8

Calling f(; a = 1, b = 2, c = 3) what is kwargs1 and kwargs2?.


#9

Well calling would have to be f(; a = 1, b = 2; c = 3 ) also.


#10

What is the use case?


#11

Now it leaks out to the caller though while it seems like the usecase for this should just be a convenience for the function itself.


#12

For example writing a function that calls an optimization routine, as well as an ODE solve and a plotting routine which may share same keyword arguments, then one could pass the keywords to those separately.
But maybe namedtuples is a better approach in this case anyways.


#13

you can have defaults with a namedtuple

julia> function f(nt::NamedTuple{(:a,:b,:c)}=(a=1, b=2.0, c="three"))
          return nt
       end
f (generic function with 2 methods)

julia> f((a=2, b=3.0, c="four"))
(a = 2, b = 3.0, c = "four")

julia> f()
(a = 1, b = 2.0, c = "three")