First, those who say that you should avoid evaluating expressions at runtime are absolutely correct. Don’t do it if there’s any other solution.
If you do need to do it, and there are situations where it is valid, you have two sane options to make it semantically local, even though the eval
takes place in global scope.
- Eval an anonymous function and pass your arguments to it.
julia> function Compare(a, b, c::String)
f = eval(Meta.parse("(a, b) -> $c"))
return Base.invokelatest(f, a, b)
end
Compare (generic function with 1 method)
julia> Compare(1, 1, "a>b")
false
The invokelatest
call does the same thing as f(a, b)
but is necessary for technical reasons, basically because it calls a function that didn’t exist when Compare
itself was called.
- Inject your arguments into the expression before evaluating it. This has been demonstrated in earlier replies but let’s do it in a more general way, reusing a solution from Evaluate expression with variables from a dictionary - #2 by GunnarFarneback :
interpolate_from_dict(ex::Expr, dict) = Expr(ex.head, interpolate_from_dict.(ex.args, Ref(dict))...)
interpolate_from_dict(ex::Symbol, dict) = get(dict, ex, ex)
interpolate_from_dict(ex::Any, dict) = ex
function Compare(a, b, c::String)
expr = Meta.parse(c)
return eval(interpolate_from_dict(expr, Dict(:a => a, :b => b)))
end