Hi,
how does one unquote and use a variable from outer scope in nested defmacro?
I want to have a macro, which when used like def_loggers(:core) which expands to another defmacro like defmacro core_log(format_str)
Reason for this is because I want to inject MODULE inside the parameter list… (I know there’s Logger lib, which can do that, but this is for work where I can’t change it)…
So, my attempts so far were:
1.)
defmacro def_loggers(log_prefix) do
macro_name = String.to_atom("#{log_prefix}_log")
quote bind_quoted: [prefix: log_prefix, name: macro_name] do
defmacro unquote(name)(format) do
quote do
vlinfo(unquote(unquote(prefix)), unquote("~s: " <> format), [__MODULE__])
end
end
end
end
Problem is, that when I want to use def_loggers macro like this:
defmodule Xxx, do: Rezolve.XLager.def_loggers(:core)
so that I can use log macro core_log like this:
Xxx.core_log("TEST")
I’m hitting this error:
iex(core_1@elixir-node_1)55> defmodule Xxx, do: Rezolve.XLager.def_loggers(:core)
warning: redefining module Xxx (current version defined in memory)
iex:55
** (CompileError) iex:55: unquote called outside quote
(stdlib) lists.erl:1354: :lists.mapfoldl/3
(stdlib) lists.erl:1355: :lists.mapfoldl/3
iex:55: (module)
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(iex) lib/iex/evaluator.ex:231: IEx.Evaluator.handle_eval/5
Not quite sure why - the unquote(unquote(prefix)) - 2 levels of unquoting is inside 2 levels of quoting introduced by 2 surrounding defmacro’s.
With only one level of unquoting of prefix variable like this:
defmacro def_loggers(log_prefix) do
macro_name = String.to_atom("#{log_prefix}_log")
quote bind_quoted: [prefix: log_prefix, name: macro_name] do
defmacro unquote(name)(format) do
quote do
vlinfo(unquote(prefix), unquote("~s: " <> format), [__MODULE__])
end
end
end
end
The compile error makes more sense:
iex(core_1@elixir-node_1)60> defmodule Xxx, do: Rezolve.XLager.def_loggers(:core)
warning: redefining module Xxx (current version defined in memory)
iex:60
** (CompileError) iex:60: undefined function prefix/0
(stdlib) lists.erl:1338: :lists.foreach/2
But of course this isn’t the right way to go as well.
Any ideas?
Thanks,
Karol