Passing Keyword Arguments to Expr Object with :call


#1

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}?


#2

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)

#3

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