Scoping issues with Macro in Module

Hi everyone,

I need to create a mutable struct where I do not know the number of fields a priori. I could alternatively just use a NamedTuple for this, but in this case the struct is preferable to me. I can do this wonderfully already with the nice QuickTypes module:


using QuickTypes
param_names = (:μ, :σ, :p)
param_values = [1., 2., [.2, .3, .5] ]
@eval @qstruct_fp MyModel($(param_names...) )
MyModel(param_values...)

However, when I pack this into a module, I run into scoping issues, and no matter how much I try to solve this I do not manage to succeed. I already searched this forum and Google without success. An example code is provided below:

module TestModule
## Load and reexport QuickTypes
    using Reexport
    @reexport using QuickTypes
## Define Macro
    abstract type Supertype1 end
    macro make_new_struct(modelname, parametername)
        esc(:( @eval @qstruct_fp $modelname($(parametername...) ) <: Supertype1 ) )
        #do some other stuff
    end
## Export everything
    export @make_new_struct
    export Supertype1 
end

using Main.TestModule
TestModule.@make_new_struct(MyNewName, param_names) #LoadError: MethodError: no method matching iterate(::Symbol

I would be really relieved if someone could help me solve this issue!

I am not sure you are hitting scoping issues. In fact, I am not sure if your macro makes sense. The problem seems to happen because of the parametername... inside your macro. The iterate fails because you are trying to splat a Symbol (splat uses iterate to expand parametername, but parametername is a Symbol not a list). It seems to me that you misunderstand how macros work, they receive the code you wrote as arguments, not the values inside any variables (macros cannot receive values). Therefore parametername is just a symbol :param_names inside your macro, and your macro has not access to any information of what would be inside a variable with the same name in runtime.

1 Like

Thank you for your reply! I am indeed not knowledgeable in this area. However, what @qstruct_fp does is just creating a parametric struct, i.e.

a #UndefVarError: a not defined
b #UndefVarError: a not defined
@qstruct_fp test123(a,b)
test123(1., 2) #test123{Float64,Int64} 

so when I call this Macro I actually dont need the values, but just the fieldnames - which is a case for Macros I assume?

If there are more elegant methods for creating fully parametric structs from a given vector/tuple of fieldnames, then I would be open to learn from that.

I think your code may work (did not test it), but then you need to pass the field names directly to the macro, instead of hiding them as values inside a variable (in this your macro will only see the variable name). Did you consider taking a variable number of arguments in the macro? (i.e., appending ... on the parameter parametername)

I think your example with @eval @qstruct_fp MyModel($(param_names...) ) worked because you splatted and interpolated the param_names in the scope it is defined (and not inside the macro).