[英]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.