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
为什么?
我认为那是因为 你取消引用(绑定)变量 name
.
的地方
在第一种情况下,您在创建模块时取消引用变量 name
,因此此时绑定变量需要检查上下文(检查代码是否在另一个模块中,例如).因此,您将获得当前原子加上适当的上下文:Runner.Foo
.
在第二种情况下,您在将变量 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
我有一个定义模块的宏。
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
为什么?
我认为那是因为 你取消引用(绑定)变量 name
.
在第一种情况下,您在创建模块时取消引用变量 name
,因此此时绑定变量需要检查上下文(检查代码是否在另一个模块中,例如).因此,您将获得当前原子加上适当的上下文:Runner.Foo
.
在第二种情况下,您在将变量 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