Stack Overflown
Stack Overflown

Reputation: 702

Evaluating expression on tuple of variables doesn't work inside function

I'm trying to write a function that takes in an expression and a set of variables with values assigned, and outputs the expression evaluated on those variables. I came up with the following code, which works by constructing an anonymous function with keyword arguments and calling it on the splatted tuple of variables.

function eval_vars(expr::Expr, vars::NamedTuple)
    func = eval(Expr(:->, Expr(:parameters, keys(vars)...), expr))
    return func(; vars...)
end

As an example, suppose I define ex = :(a^2 + b^2) and vs = (a=3, b=4). Then I want eval_vars(ex, vs) to return 25.

What's really odd is when I run these lines one by one I get what I'm expecting:

julia> ex = :(a^2 + b^2)
:(a ^ 2 + b ^ 2)

julia> vs = (a=3, b=4)
(a = 3, b = 4)

julia> func = eval(Expr(:->, Expr(:parameters, keys(vs)...), ex))
#50 (generic function with 1 method)

julia> func(; vs...)
25

But when I call eval_vars, I get an error:

julia> eval_vars(ex, vs)
ERROR: MethodError: no method matching (::var"#53#55")(; a=3, b=4)
Closest candidates are:
  (::var"#53#55")(; a, b) at none:0
Stacktrace:
 [1] eval_vars(expr::Expr, vars::NamedTuple{(:a, :b), Tuple{Int64, Int64}})
   @ Main ./REPL[46]:3
 [2] top-level scope
   @ REPL[47]:1

Why is this a MethodError if the candidate mentioned is an exact match? Why does this code work on its own but not when inside of a function?

Upvotes: 3

Views: 63

Answers (1)

Bogumił Kamiński
Bogumił Kamiński

Reputation: 69839

You are likely hitting wolrd-age problem. The solution is:

function eval_vars(expr::Expr, vars::NamedTuple)
    func = eval(Expr(:->, Expr(:parameters, keys(vars)...), expr))
    return Base.invokelatest(func; vars...)
end

However, in general, macros are preferred for manipulating Julia expressions.

Upvotes: 2

Related Questions