Hi, short history:
I define a macro in a module, it works fine, but when I call this macro inside a for it fails.
Here a full MWE.
## ----------------------------------------------------------------
# I wants a function '_gen_macro' that generate/eval a macro in a module 'mod'
function _macro_ex(mod, exs...)
quote
$(mod).foo($exs) # 'foo' is expected to be in the module
end
end
function _gen_macro(mod)
@eval mod begin
macro _macro(ex, exs...)
$(_macro_ex)($mod, ex, exs...)
end
end
end
## ----------------------------------------------------------------
# MWE
module A
import Main: _gen_macro
foo(exs...) = @info("In Mod A", length(exs...))
_gen_macro(A)
end
## ----------------------------------------------------------------
# Tests (eval each section in sequence, not at the same time)
## this works
let
A.@_macro 1 2 3 4
end
# Output
# β Info: In Mod A
# β length(exs...) = 4
## But this fails before run time (I think at macro-expansion?)
for Mod in [A]
@info("This won't be printed")
Mod.@_macro 1 2 3 4
end
# Output
# ERROR: LoadError: LoadError: UndefVarError: Mod not defined
## But 'Mod' is defined and has '@_macro' in it...
for Mod in [A]
has_macro = isdefined(Mod, Symbol("@_macro"))
Mod_names = join(string.(names(Mod; all = true)), ", ")
@info("This will be printed", Mod, has_macro, Mod_names)
end
# Output
# β Info: This will be printed
# β Mod = Main.A
# β has_macro = true
# β Mod_names = "#@_macro, #eval, #foo, #include, @_macro, A, eval, foo, include"
Hi, thanks. Can you elaborate further?
This is how I think stuff happens. I call eval only inside the function _gen_macro, I call this function when I define the module A, so it define in the global scope of A the macro @_macro. Later I even use A.@_macro successfully. What I donβt understand is why the for loop fails. The error occurs even before the printing line is executed so I suspect any code inside A runs either. Also, the error said Mod is not define, it is like the code inside the loop is being run in a scope other than the loop.
WOW, thanks, I didnβt know that @Mod.macroname is equivalent to Mod.@macroname.
But, there is any workaround?
My goal would be:
To call a macro call _macro contained in several modules listed on Mods with the same expression as argument.
Yes but that will eval the expression at the global scope of the module.
The real macro that Iβm using do assign new variables, so I want such variables to remain in the scope where I called the macro. At the same time the same macro is defined in several modules, and behave different depending on the module.
For example I want this code:
let
@A._macro a
@B._macro b
end
to expand to:
let
a = "From module A"
b = "From module B"
end
Implicitly im using the module as the first argument of the macro. Iβm maybe using an antipattern.
This is maybe more julian.
## ----------------------------------------------------------------
# Now the `_macro` will be defined globally and accept the module as argument
function _macro_ex(mod, exs...)
quote
$(esc(mod)).foo($(exs)) # 'foo' is expected to be in the module
end
end
# Im expecting all arg to be ok
macro _macro(mod, ex, exs...)
_macro_ex(mod, ex, exs...)
end
## ----------------------------------------------------------------
# MWE
module A
import Main: _gen_macro
foo(exs...) = @info("In Mod A", length(exs...))
end
## ----------------------------------------------------------------
# Tests (eval each section in sequence, not at the same time)
## this works
let
@_macro A 1 2 3 4
end
# Output
# β Info: In Mod A
# β length(exs...) = 4
## This is now working
for Mod in [A]
@info("This is now printed")
@_macro Mod 1 2 3 4
end
# Output
# [ Info: This is now printed
# β Info: In Mod A
# β length(exs...) = 4