How to compile Expr to function?


#1

I’m trying to implement the following scenario: based on some configuration, I want to build an expression which essentially defines a function:

function build(config::String)
    :(function p() println($config) end)
    # or :(() -> println($config))
end

ex = build("test")

And then pass the compiled function to another method:

function test(f::Function)
    f()
end

test(eval(ex))
# prints "test"

The eval however has a side effect of introducing function p() (or if it has no name, then anonymous function) in the current global scope. Does anybody know how to achieve the same goal while not introducing additional definitions to the global scope, like

const f = compile(ex)
f()
# prints "test"
methods(p)
# p not defined

#2

you can do:

module MyEvaledFuncs

end

f = eval(MyEvaledFuncs, expr)

#3

Thank you for your suggestion, but this:

function build(config::String)
    function p() println(config) end
end

const f = build("test")
f()

never introduces p() to global scope (or module scope to be precise). However I need more flexibility and construct the body of this function dynamically. Are you aware of any other methods which might be helpful here?


#4

Be aware that you cannot call the function immediately without going back to global scope again, don’t do this too many times and then you can use

function build(config::String)
    @eval function () println($config) end
end

#5

Have you checked out generated functions?


#6

Yes, this is so far the only solution I know, with the only drawback that with each call it adds a new anonymous function to a global scope (current module). When working with a normal nested function, nothing extra goes outside, so I wondered whether the same is possible with eval.

Actually it is interesting whether this is a viable workaround, as suggested by @sdanisch:

function build(config)
    ex = make_body(config)
    temp = Module()
    eval(temp, :(() -> $ex))
end

but I suspect that this anonymous module will be hanging around till the end.


#7

Thank you for suggestion, but generated functions seem to only provide information about the type of arguments, while I build an expression at runtime based on some config object.


#8

Why return an expression? Why not just return a function?

build(config::AbstractString) = () -> println(config)

#9

Because function build is supposed to build a function body (or entire function), based on a passed config. I just provided a minimalistic example. It could be as well something like this:

function build(config::Config)
    body = Expr(:block)
    fexpr = Expr(:function,:(make(template::Template)), body)
    push!(body.args, :(local obj::SomeObj))
    current_block = body
    for i in 1:length(config.types)
        mexpr = config.construct_expr[i]
        code = sd.codes[i]
        elseexpr = Expr(:block)
        ifexpr = Expr(:if, :(template.code == $code), :(obj = $mexpr), elseexpr)
        push!(current_block.args, ifexpr)
        current_block = elseexpr
    end
    push!(body.args, :(return obj))
    fexpr
end