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

Eval works in the global scope and can be tricky to use within functions. Changing your definition such that it creates a function with positional arguments gives a hint

julia> function eval_vars(expr::Expr, vars::NamedTuple)
           foo = eval(Expr(:->, Expr(:tuple, keys(vars)...), expr))
           @show methods(foo)
           return foo(values(vars)...)
       end
eval_vars (generic function with 1 method)

julia> eval_vars(ex, vs)
methods(foo) = # 1 method for anonymous function "#62":
[1] (::var"#62#63")(a, b) in Main
ERROR: MethodError: no method matching (::var"#62#63")(::Int64, ::Int64)
The applicable method may be too new: running in world age 32477, while current world is 32478.
Closest candidates are:
  (::var"#62#63")(::Any, ::Any) at none:0 (method too new to be called from this world context.)

I.e., the created function cannot be called immediately, but only after your function returns:

julia> function eval_vars(expr::Expr, vars::NamedTuple)
           return eval(Expr(:->, Expr(:parameters, keys(vars)...), expr))
       end
eval_vars (generic function with 1 method)

julia> eval_vars(ex, vs)(; vs...)
25

The simplest way to evaluate everything within a function, is to create an expression which directly computes the desired result when evaluated, e.g.,

julia> function eval_vars(expr::Expr, vars::NamedTuple)
           eval(:(let $(map((k, v) -> :($k = $v), keys(vars), values(vars))...); $expr end))
       end
eval_vars (generic function with 1 method)

julia> eval_vars(ex, vs)
25
1 Like