How to nest splicing contexts (i.e. @eval inside quote block)

I am trying to make a macro that needs to evaluate an arbitrary (not known at parse time) amount of iterations of some code, which will change each time. I am trying to figure out how you would separately splice into the different levels of quoting. Here is a rough example of this

macro somemacro(var1)
    return quote
        for var2 in foo($var1)
            @eval @othermacro $var2 $var1
        end
    end
end

I also saw this post, but it didn’t really have an answer.

Also thought about trying to make @othermacro just capture var2 directly instead of receiving it, to avoid this problem, but I can’t figure out how to make that work either.

This sounds impossible. If the number of iterations is not known at parse time, it can’t be known to the macro either, since that’s when macros are expanded. In particular, in your code example @othermacro is expanded once, before the for loop is run.
If you want to evaluate the macro a constant number of times (given as a literal) you can do

macro somemacro(n)
    Expr(:block, (:(@othermacro $i) for i=1:n)...)
end
macro othermacro(x)
    println("othermacro $x")
    :("othermacro",$x)
end

Now

julia> @macroexpand @somemacro 3
othermacro 1
othermacro 2
othermacro 3
quote
    ("othermacro", 1)
    ("othermacro", 2)
    ("othermacro", 3)
end

I’m a bit at a loss here, trying to understand what you want to do, and exactly what your problem is…

Do you think you could elaborate a bit on your example? In particular:

  • what should the macro call look like?
  • what should the macro expand to?

And IIUC the inner @othermacro macro is written by you. Does it have to be a macro? Is it called in an other context elsewhere, or did you just write it for the purpose of using it inside @somemacro?

2 Likes

I was basically trying to hack together a version of what ImportMacros.jl does and trying to manually bind the symbols from calling names on some module to local variables in the calling scope.

I think I pretty much figured out better ways to do this (export the actual for loop from the macro and abuse Meta.parse and the string constructor and eval), but with this question I am still curious if it is possible to have different depths of interpolation for nesting quoting calls. I’ve noticed that quote blocks leave string interpolations un-interpolated so that the actual interpolation can be done at evaluation time, but I can’t find any clear way for raw code in a quote block to be left un-interpolated so that a later quote block can pick it up.

Edit: There was a better way to do what I was doing by using a loop in the macro to build up the expressions and splatting it into another expression with :toplevel then just interpolating the whole thing into the output to avoid extra eval or Parse statements, so perhaps the failure to get that nested quoting to work was for the best, but I still think this is an area of ambiguity.