鉴于以下内容:
for fn_name <- [:foo, :bar, :baz] do
defmacro unquote(fn_name)(do: inner) do
fn_name = unquote(fn_name) # <--- Why?
quote do
IO.puts "#{unquote(fn_name)} called"
unquote(inner)
end
end
end
fn_name = unquote(fn_name)
的原因是什么?如果我省略这一行,那就是编译错误。这是什么原因&#34; double&#34; unquoting?
答案 0 :(得分:8)
让我们稍微简化一下这个例子:
for fn_name <- [:foo, :bar, :baz] do
defmacro unquote(fn_name)(do: inner) do
fn_name = unquote(fn_name) # <--- Why?
quote do
{unquote(fn_name), unquote(inner)}
end
end
end
在上面的例子中,因为quote返回一个带有两个不带引号的元素的元组,它相当于:
for fn_name <- [:foo, :bar, :baz] do
defmacro unquote(fn_name)(do: inner) do
fn_name = unquote(fn_name) # <--- Why?
{fn_name, inner}
end
end
现在更容易理解如果你之前没有unquote(fn_name)
会发生什么:变量fn_name
根本不会存在于宏定义中。请记住,所有def
s(def,defp,defmacro等)都会启动一个新的变量作用域,因此如果要在内部使用fn_name,则需要以某种方式定义它。
我们在此代码中看到的另一个属性是Elixir在看到quote
时将停止取消引用。因此,在上面的引用中,unquote
在定义宏时不会被引用,而是在宏执行时,这也解释了为什么需要在宏内部定义变量。
答案 1 :(得分:2)
这是因为卫生。
<\ n> Elixir具有宏卫生的概念。卫生意味着您在宏中定义的变量,导入和别名不会泄漏到调用者自己的定义中。for fn_name <- [:foo, :bar, :baz] do
defmacro unquote(fn_name)(do: inner) do
fn_name = unquote(fn_name) # <-- This is macro's context
quote do
IO.puts "#{unquote(fn_name)} called" # <-- This is caller's context
unquote(inner)
end
end
end
你应该阅读Chris McCord的Metaprogramming Elixir书中的 Hygiene保护来电者的背景