Passing Keyword Arguments to Expr Object with :call

I have a function f that has many different versions (e.g. f_A, f_B, f_C, …), “subversions” (e.g. f_Aa, f_Ab, …, f_Ba, f_Bb, …), “subsubversions” (e.g. f_Aaa, …), and so forth. Each of these takes a large number of parameters, most of which they have in common but some they don’t. I want to test the different versions of this function in a loop because there are so many of them. Perhaps something like:

f_A(;x, y, unused...) = x + y
f_B(;x, z, unused...) = x + z
function f(version; kwargs...)
    return eval(Expr(:call, Symbol("f_", version), kwargs...))
end

for ver = ["A", "B"]
    f(ver, x=1, y=2, z=3) # Throws error
end

It seems that the keyword arguments are passed to the function f_A or f_B as Pairs instead of kw arguments. For example, using range as an example of a built-in function with keyword arguments,

expr1 = Meta.parse("range(1, stop=2, length=5)")
Meta.dump(expr1)

Returns the following:

Expr
head: Symbol call
args: Array{Any}((4,))
1: Symbol range
2: Int64 1
3: Expr
head: Symbol kw
args: Array{Any}((2,))
1: Symbol stop
2: Int64 2
4: Expr
head: Symbol kw
args: Array{Any}((2,))
1: Symbol length
2: Int64 5

Whereas my expression looks like this:

function get_expr(version; kwargs...)
    return Expr(:call, Expr(Symbol("f_", version), kwargs...))
end
expr2 = get_expr("A", x=1, y=2, z=3)
Meta.dump(expr2)

Returns:

Expr
head: Symbol call
args: Array{Any}((1,))
1: Expr
head: Symbol f_A
args: Array{Any}((3,))
1: Pair{Symbol,Int64}
first: Symbol x
second: Int64 1
2: Pair{Symbol,Int64}
first: Symbol y
second: Int64 2
3: Pair{Symbol,Int64}
first: Symbol z
second: Int64 3

So how can I get the keyword arguments to be passed to f_A or f_B as Symbol kw instead of Pair{Symbol,Int64}?

You’re gonna need something like

map(pair → Expr(:kw, pair.first, pair.second), kwargs)

But before you do this, I have a couple of suggestions:

  1. Implement this as a macro instead
  2. Refactor your functions. In general, using postfixes to create a family of related functions in programming suggests that you have a problem which could better be solved by multiple dispatch (and the devs feel so strongly about this that they’ve nixed underscores from Base)

If it is just used there in the testing, then a macro is not needed. Just a loop with evals is the ticket.