繁体   English   中英

Elixir宏和bind_quoted

[英]Elixir macros and bind_quoted

我有一个定义模块的宏。

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

运行它的输出是:

I am Bar
I am Runner.Foo

这是有道理的; 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

输出现在变为:

I am Bar
I am Foo

为什么??

我想,那是因为你在哪里 unquoting(绑定)变量的地方name

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

在第二种情况下,在将变量name放置在上下文之前,您将取消该变量name ,因此其值不会更改,它将是原子Foo (无上下文)。

使用此代码,您将看到用于创建模块的正确值:

require Logger

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

defmodule MacroFun do

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

  defmacro define_module2(name) do
    quote bind_quoted: [name: name] do
      defmodule name do
        import Bar
        Logger.debug("#{inspect name}")
        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
  def run2 do
    MacroFun.define_module2 Foo2
    Foo2.foo
  end

end

Runner.run
Runner.run2

输出:

[warn]  Foo
[warn]  Runner.Foo
I am Bar
I am Runner.Foo

[warn]  Foo2
I am Bar
I am Foo2

暂无
暂无

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

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