Stumped on eval of function with parameters

I am trying to provide a feature to allow a user to supply a function expression and have that expression evaluated at runtime.

The function needs two arguments, m and n. Both are assigned values before the evaluation.

function calculate(e::Expr)
   m = rand(1:4, 5, 5)
   n = rand(0:1, 5, 5)
   return eval(e)
end

Why does the following not work?

d = :(rank(m * n))
julia> calculate(d)
ERROR: UndefVarError: m not defined
Stacktrace:
 [1] top-level scope at none:0
 [2] eval at ./boot.jl:330 [inlined]
 [3] calculate(::Expr) at ./none:6
 [4] top-level scope at none:0

Thanks!

Why not use a function or function closure like that:

function calculate(f::Function)
    m = rand(1:4, 5, 5)
    n = rand(0:1, 5, 5)
    f(m, n) 
end
julia> using LinearAlgebra

julia> d(m,n) = rank(m*n)
d (generic function with 1 method)

julia> calculate(d)
4

julia> calculate((m,n)->rank(m*n))
4

wouldn’t that be faster, simpler and more idiomatic?

4 Likes

The technical reason is that eval works in global scope. But as already been said, passing higher order functions is almost always the better API in cases like this.

Yes! Thank you. Using Function directly as the type did the trick.

calculate(reducef::Function = (m,n,f)->rank(m * n), ...) # this is the default function
…then…
reducef(m, n, f)

@kristoffer.carlsson - I’m still not clear why eval() fails to work, even if I use eval(mymodule, expr). Are there built-in functions of Julia that can clarify the context in which code is evaluated at runtime? Very powerful stuff, this metaprogramming, but powerfully complicated as well. :wink: