Evaluating a SyntaxTree.genfun within a function

Im trying to evaluate a string as a function within a function. I have made good progress:

using SyntaxTree
function Evaluate(some_function::String ; variables = [:x])
    g = genfun(Meta.parse(some_function), variables)
    return g
end

This allows me to write Evaluate(“some function”)(x)
which is good, but if I want to evaluate this inside another function, for example:

function foo(some_function::String ; variables = [:x])
     x = 1
     return Evaluate(some_function)(x)
end

I get an Error

MethodError: no method matching (::getfield(SyntaxTree, Symbol("##137#138")))(::Float64)
The applicable method may be too new: running in world age 26255, while current world is 26256.
Closest candidates are:
  #137(::Any) at /Users/joe/.julia/packages/SyntaxTree/Adq4Y/src/SyntaxTree.jl:144 (method too new to be called from this world context.)

Stacktrace:
 [1] h(::Float64) at ./In[113]:1
 [2] WaveFunctionNumerov(::String, ::Float64, ::Int64, ::Float64, ::Float64, ::Float64) at ./In[113]:24
 [3] top-level scope at In[114]:52

Why is this a problem in the first place and how can I get around it?

the evaluation really needs to be within a function

1 Like

First thing first: why?
I ask because I see people try to do this a lot, and it has never been the right answer.

That being said, it looks like SyntaxTree offers genlatest which should work, since it calls Base.invokelatest, getting around the world age issue. Again, this is probably the wrong approach to the problem, with plenty of negative performance and runtime consequences, so I urge you to reconsider.

2 Likes

Hi, i am the creator of SyntaxTree and I recommend using Julia expressions for metaptogramming if possible instead of parsing strings, as @tomerarnon mentioned it is not so good to use strings for this in general.

Indeed, the purpose of genlatest is to get around the world age issues.

3 Likes

The problem is that genfun most likely uses eval and eval always runs at runtime. Therefore, when the compiler looks at foo, eval has not been run yet, so it doesn’t have any way of knowing what to do with the call Evaluate(some_function)(x). This is what it means for g to only be defined in a newer world age. You can use Base.invokelatest, to work around this, but that has quite a big performance penalty. An alternative might be to use RuntimeGeneratedFunctions.jl, which can build functions from expressions at runtime without the use of eval, but is a bit hacky.

1 Like

The idea is that when running the julia script, the script will ask for an input, (in the form of a String or Expr) and then run the program from there making the program more dynamic and diverse.

I will try the genlatest, although I’d be more than happy to hear a more effective solution if you have one?

Why would this make a difference? Surely parsing a String into an Expr is effectively the same thing as just taking an Expr as input?

True, in this case using a string or an expr would be the same (and suffer from the same limitations).
If you intend to create a function from a user inputted string, consider separating the Evaluate function into two separate operations in your script. First create the genfun function (which will no longer need to be genlatest, as a result of this split) and then pass the created function on to another function to do the work. This will ensure type stability and will therefore be more performant.

g = Evaluate(user_input) # type unstable, but who cares?
foo(g) # foo is specialized to typeof(g) now!
2 Likes