Call macro inside a macro with external variables escaped

I have a problem making the following example work, it tells me @option & VectorOption are not defined:

module MWE

using Configurations: @option

export @vopt, vopt

abstract type VectorOption end

macro vopt(type, alias)
    return esc(vopt(type, alias))
end

function vopt(type, alias)
    return quote
        @option $alias struct $type <: VectorOption
            values::Vector{Float64}
        end
    end
end

end
julia> Main.MWE.@vopt Pr "pr"
ERROR: UndefVarError: @option not defined

julia> Main.MWE.@vopt Pr "pr"
ERROR: UndefVarError: VectorOption not defined
Stacktrace:
 [1] macro expansion
   @ ~/.julia/packages/ExproniconLite/5zV9Q/src/codegen.jl:101 [inlined]
 [2] macro expansion
   @ ~/.julia/packages/Configurations/1TQp9/src/codegen.jl:121 [inlined]
 [3] top-level scope
   @ REPL[1]:15

I have tried several different ways of rewriting vopt and @vopt, but none of them work.


To run this example, you may need to install Configurations.jl.

Since you’re escaping the whole thing, @option and VectorOption get evaluated as Main.@option and Main.VectorOption respectively. Writing them explicitly as being from MWE i.e.

 MWE.@option $alias struct $type <: MWE.VectorOption

works for this minimal example, would that work for your real use case too? (For MWE.VectorOption you can also write $VectorOption instead, but I don’t know how to interpolate a macro invocation like @option with a $.)

1 Like

Thank you @digital_carver, adding MWE to both of them works. However, since @option is defined in Configurations.jl and VectorOption is already defined in MWE, it feels strange to invoke them as MWE.@option and MWE.VectorOption to me.

If you want to avoid spelling the module name, you can interpolate non-macro objects into the expressions directly, like so:

quote
    @option $alias struct $type <: $VectorOption
        values::Vector{Float64}
    end
end

The $VectorOption becomes Main.MWE.VectorOption.

Macros are more annoying to specify dynamically, because they have special syntax. If you really really want to avoid writing

quote
    MWE.@option $alias struct $type <: $VectorOption
        values::Vector{Float64}
    end
end

like you should, you could do

quote
    $(__MODULE__).@option $alias struct $type <: $VectorOption
        values::Vector{Float64}
    end
end
1 Like