Interpolating a Dict with Exprs inside a quoted expression

I am coding some macros and in one of them I end up with a Dict{Symbol, Tuple{Symbol, Expr}}, where the Exprs are all supposed to be interpolated into the expression returned by the macro. What I am doing right now is this:

dict = Dict{Symbol, Tuple{Symbol, Expr}}()
# some code, filling up the dict...
quote
    $(some_fn)(
        $arg,
        $arg2,
        Dict{Symbol, Tuple{Symbol, DataType}}(
            [p.first => (p.second[1], eval(p.second[2])) for p in $(dict)]
        )
    )
end

But this has the drawback of eval working in the topmost scope, and I want this macro usable at module-level or from the REPL. Is there a way to interpolate all of those Exprs from the Dict’s values into a Dict constructor? Or am I doing this wrong and maybe there’s a better way of achieving this?

You probably want something like:

quote
    $(some_fn)(
        $arg,
        $arg2,
        Dict{Symbol, Tuple{Symbol, DataType}}(
            $([:($(QuoteNode(p.first)) => ($(QuoteNode(p.second[1])), $(p.second[2]))) for p in dict]...)
        )
    )
end

This should interpolate the expressions into the quote directly, so you don’t have to ressort to using eval.

Thanks! I didn’t really realize that I can broadcast a vector/put a tuple into $( ) and that it would work like that, good to know!

Actually, I tested this with the macro being defined in a module in a file I included in the REPL and this doesn’t actually work, generating an UndefVarError. In your solution the p.second[2] expressions are evaluated inside the macro scope, and not the caller’s scope. If you define the macro inside the REPL then actually the macro’s scope uses symbols from the scope above, where all the stuff you define in the REPL lies, so the variables are found.
In short, this doesn’t work because the interpolated expressions are evaluated in the wrong scope.

If you are returning this from a macro, you need to use esc to allow macro hygiene to resolve these bindings correctly, see:

https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene

1 Like

OK, now all’s working, thanks for help!