Elixir宏和bind_quoted

cjb*_*aro 8 macros metaprogramming elixir

我有一个定义模块的宏.

defmodule Bar do
  def bar do
    IO.puts "I am #{inspect __MODULE__}"
  end
end

defmodule MacroFun do

  defmacro define_module(name) do
    quote do
      defmodule unquote(name) do
        import Bar
        def foo do
          bar
          IO.puts "I am #{inspect __MODULE__}"
        end
      end
    end
  end

end

defmodule Runner do
  require MacroFun

  def run do
    MacroFun.define_module Foo
    Foo.foo
  end

end

Runner.run
Run Code Online (Sandbox Code Playgroud)

运行它的输出是:

I am Bar
I am Runner.Foo
Run Code Online (Sandbox Code Playgroud)

这是有道理的; MacroFun.define_module被调用,Runner.run因此模块被定义并因此嵌套在Runner模块下.

但是现在如果我MacroFun.define_module改为使用:bind_quoted选项:

  defmacro define_module(name) do
    quote bind_quoted: [name: name] do
      defmodule name do
        import Bar
        def foo do
          bar
          IO.puts "I am #{inspect __MODULE__}"
        end
      end
    end
  end
Run Code Online (Sandbox Code Playgroud)

输出现在变为:

I am Bar
I am Foo
Run Code Online (Sandbox Code Playgroud)

为什么??

Ale*_*usa 7

我认为这是因为这个地方在那里你unquoting(绑定)变量name.

在第一种情况下,您name在创建模块时取消对变量的引用,因此在此时绑定变量需要检查上下文(例如,检查代码是否在另一个模块中).所以,你得到你当前的原子加上适当的上下文:Runner.Foo.

在第二种情况下,您在将变量name置于上下文之前对其进行取消引用,因此其值不会更改,它将是原子Foo(无上下文).

  • 哇,这很微妙,但有道理.回顾一下以确保我理解:当使用`bind_quoted`时,`name`是不引用_outside_引用的正文(`do`..`end`).但是当不使用`bind_quoted`时,`name`在引用中明确地不加引号. (2认同)