繁体   English   中英

为动态生成的宏加双引号

[英]Double unquoting for dynamically generated macros

给定以下内容:

  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)的原因是什么? 如果我省略此行,则为编译错误。 这种“双重”取消报价的原因是什么?

让我们稍微简化一下示例:

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 (def,defp,defmacro等)都会启动一个新的变量作用域,因此,如果要在内部使用fn_name,则需要以某种方式对其进行定义。

我们在此代码中看到的另一个属性是Elixir在看到quote时将停止取消quote 因此在上面的报价, unquote的宏定义,而是在执行宏,这也解释了为什么需要变量的宏内部被定义时,不会加引号。

这是因为卫生

长生不老药有宏观卫生学的概念。 卫生意味着您在宏中定义的变量,导入和别名不会泄漏到调用者自己的定义中。

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书中阅读“ 卫生保护呼叫者的上下文

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM