'UndefVarError: x not defined' when calling a macro outside of its module


#1

I’m trying to code a macro which calls a function defined within the same module (with Julia v1.1). I always get the ERROR: LoadError: UndefVarError: myModel not defined.

The code of the module is the following (Models.jl):

module Models

export Model
export createmodel
export add_variable
export @add_variable

mutable struct Model
    variable_symbols::Dict{String, Symbol}
    variable_data::Dict{Symbol, Array{Int64, 1}}
    Model(; variable_symbols = Dict([]), variable_data = Dict([])) = 
        new(variable_symbols, variable_data)
    Model(variable_symbols, variable_data) = new(variable_symbols, 
        variable_data)
end

function createmodel()
    model = Model()
    return model
end

function add_variable(model, label::String, value::Array{Int64, 1})
    symbol = Symbol(label)
    model.variable_symbols[label] = Symbol(symbol)
    model.variable_data[symbol] = value
end

macro add_variable(model, var)
    quote
        add_variable($model, $(string(var)), $var)
    end
end

end

The macro is called as following:

include("Models.jl")
using .Models

x = [1, 2, 3]

myModel = createmodel()
@add_variable(myModel, x)
show(myModel)

I know that this is an escaping/hygiene problem, but after trying many ideas I don’t get the expected result. Up to now, my only way to get the expected result was to define the macro outside of the module to get: Model(Dict("x"=>:x), Dict(:x=>[1, 2, 3]))


#2

[SOLVED]

macro add_variable(model, var)
    quote
        add_variable($(esc(model)), $(string(var)), $(esc(var)))
    end
end

#3

You’re right that this is an escaping problem. Here’s how to fix it:

macro add_variable_fixed(model, var)
    quote
        add_variable($(esc(model)), $(string(var)), $(esc(var)))
    end
end

Without the esc, julia will look for the bindings myModel and var inside the module Models. To see this in action, you can use @macroexpand:

julia> @macroexpand Models.@add_variable(myModel, x)
quote
    (Main.Models.add_variable)(Main.Models.myModel, "x", Main.Models.x)
end

julia> @macroexpand Models.@add_variable_fixed(myModel, x)
quote
    (Main.Models.add_variable)(myModel, "x", x)
end

#4

Oh hey — welcome!

Try not to simultaneously cross post between Stack Overflow and here — lots of us browse both places and it just ends up duplicating efforts of volunteers.

https://stackoverflow.com/questions/54412400/undefvarerror-x-not-defined-when-calling-a-macro-outside-of-its-module-julia


#5

Thank you so much for your help Chris! It was so simple…
And all my apologies for the cross post.