如何将关键字列表发送到宏并使用bind_quoted
?以下是一个示例:
带宏的模块:
defmodule MacroTime do
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
def from_opts do
# Using opts here produces an undefined function error
IO.puts(opts[:foo])
end
end
end
end
导入模块和脚本:
defmodule Main do
use MacroTime, foo: "bar"
end
Main.from_opts
运行它会产生:
** (CompileError) main.ex:2: undefined function opts/0
您可以在此处试用:https://glot.io/snippets/eg2gg4huj3
我觉得我错过了一些关于宏的简单概念。
答案 0 :(得分:1)
我认为这是因为您引用的地方(绑定)不正确。
这是一个类似的问题: Elixir macros and bind_quoted
这是Metaprogramming Elixir一书的定义:
bind_quoted
quote宏的bind_quoted选项将绑定传递给块, 确保外部绑定变量不加引号 时间。
让我们更清楚。看看这个例子
defmodule Print do
defmacro __using__(opts) do
IO.puts "In macro's context #{__MODULE__}" # Macro context
quote bind_quoted: [opts: opts] do
IO.puts "In caller's context #{__MODULE__}" # Caller context
IO.inspect opts
def opts do
IO.puts "In function definition's context"
end
end
end
end
编译:
iex(1)> defmodule Test do
...(1)> use Print, foo: "bar"
...(1)> end
In macro's context Elixir.Print
In caller's context Elixir.Test
[foo: "bar"]
{:module, Test,
<<70, 79, 82, 49, 0, 0, 5, 24, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 127,
131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:opts, 0}}
iex(2)> Test.opts
In function definition's context
:ok
iex(3)>
要想清楚注入代码,您必须使用unquote
。
您在这里做的是将绑定变量opts
传递给引用块(来电者上下文),然后在内部调用它(函数定义的上下文)
澄清上下文的定义。这来自书:
上下文是调用者绑定,导入和别名的范围。 对于宏的调用者来说,上下文是宝贵的。它符合您的观点 对于世界而言,凭借不变性,你不要指望你的 从你下面改变的变量,导入和别名。
最后您打算使用bind_quoted
。我建议您阅读 Hygiene保护来电者的背景和 Overriding Hygiene 以考虑另一种解决方案以符合您的目的
这是我到目前为止所想到的。希望有所帮助!