0

I would like to build a Julia application where a user can specify a function using a configuration file (and therefore as a string). The configuration file then needs to be parsed before the function is evaluated in the program.

The problem is that while the function name is known locally, it is not known in the module containing the parser. One solution I have come up with is to pass the local eval function to the parsing function but that does not seem very elegant.

I have tried to come up with a minimal working example here, where instead of parsing a configuration file, the function name is already contained in a string:

module MyFuns
function myfun(a)
    return a+2
end
end

module MyUtil
# in a real application, parseconfig would parse the configuration file to extract funstr
function parseconfig(funstr)
    return eval(Meta.parse(funstr))
end
function parseconfig(funstr, myeval)
    return myeval(Meta.parse(funstr))
end
end

# test 1 -- succeeds
f1 = MyFuns.myfun
println("test1: $(f1(1))")

# test 2 -- succeeds
f2 = MyUtil.parseconfig("MyFuns.myfun", eval)
println("test2: $(f2(1))")

# test 3 -- fails
f3 = MyUtil.parseconfig("MyFuns.myfun")
println("test3: $(f3(1))")

The output is:

test1: 3
test2: 3
ERROR: LoadError: UndefVarError: MyFuns not defined

So, the second approach works but is there a better way to achieve the goal?

2 Answers 2

4

Meta.parse() will transform your string to an AST. What MyFuns.myfun refers to depends on the scope provided by the eval() you use.

The issue with your example is that the eval() inside MyUtil will evaluate in the context of that module. If that is the desired behavior, you simply miss using MyFuns inside MyUtil.

But what you really want to do is write a macro. This allows the code to be included when parsing your program, before running it. The macro will have access to a special argument __module__, which is the context where the macro is used. So __module__.eval() will execute an expression in that very scope.

foo = "outside"

module MyMod
foo = "inside"
macro eval(string)
    expr = Meta.parse(string)
    __module__.eval(expr)
end
end

MyMod.@eval "foo"

# Output is "outside"

See also this explanation on macros: https://docs.julialang.org/en/v1/manual/metaprogramming/index.html#man-macros-1

Sign up to request clarification or add additional context in comments.

3 Comments

The idea is that a user can create a new function in any new module, so importing/using MyFuns is not an option. The use of a macro looks promising though, I had seen references to __module__ in other posts here, tried it, but did not realize they only worked in macros. I'll test it now.
In that case you should run MyMod.@eval from my code example in the module that you like the user to extend.
I see, this seems to work for my example that I constructed above. In the end, I'll try to see if I can wrap my relatively complex function into a macro.
0

And for the sake of transforming the answer of @MauricevanLeeuwen into the framework of my question, this code will work:

module MyFuns
function myfun(a)
    return a+2
end
end

module MyUtil
macro parseconfig(funstr)
    __module__.eval(Meta.parse(funstr))
end
end

f4 = MyUtil.@parseconfig "MyFuns.myfun"
println("test4: $(f4(1))")

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.