[英]Using helper functions in Elixir macros
继续Elixir文档中的Binding和unquote片段示例...
我们有一个宏,用于根据关键字列表定义功能。
defmodule MacroFun do
defmacro defkv(kv) do
quote bind_quoted: [kv: kv] do
Enum.each kv, fn {k, v} ->
def unquote(k)(), do: unquote(v)
end
end
end
end
defmodule Runner do
require MacroFun
kv = [foo: 1, bar: 2]
MacroFun.defkv(kv)
end
Runner.foo
现在,让我们将宏的主体移至辅助函数。
defmacro defkv(kv) do
_defkv(kv)
end
defp _defkv(kv) do
quote bind_quoted: [kv: kv] do
Enum.each kv, fn {k, v} ->
def unquote(k)(), do: unquote(v)
end
end
end
太好了,一切仍然有效。 但是现在,如果我们要制作另一个修改kv
宏,然后再将其传递给私有帮助器函数,该怎么办:
defmacro def_modified_kv(kv) do
quote bind_quoted: [kv: kv] do
modified_kv = Enum.map kv, fn {k, v} -> {k, v + 1} end
_defkv(modified_kv)
end
end
那不行 Elixir说_devkv
没有定义。 我们可以使用完全限定的函数名称来修复:
defmacro def_modified_kv(kv) do
quote bind_quoted: [kv: kv] do
modified_kv = Enum.map kv, fn {k, v} -> {k, v + 1} end
MacroFun._defkv(modified_kv)
end
end
但是随后Elixir抱怨MacroFun._defkv
是私有的。 因此,我们将其更改为public,但是它仍然无法正常工作,因为辅助方法_devkv
将引用的代码返回到宏def_modified_kv
,该宏本身被引用了!
因此,我们可以通过评估辅助函数返回的代码(最终代码)来解决此问题:
defmodule MacroFun do
defmacro defkv(kv) do
_defkv(kv)
end
defmacro def_modified_kv(kv) do
quote bind_quoted: [kv: kv] do
modified_kv = Enum.map kv, fn {k, v} -> {k, v + 1} end
MacroFun._defkv(modified_kv) |> Code.eval_quoted([], __ENV__) |> elem(0)
end
end
def _defkv(kv) do
quote bind_quoted: [kv: kv] do
Enum.each kv, fn {k, v} ->
def unquote(k)(), do: unquote(v)
end
end
end
end
Code.eval_quoted
之外,还有更好的方法吗? 我觉得我做错了什么。
谢谢您的帮助。
导入模块时,只能导入公共方法。
宏是编译时的功能,它们在模块中实际写入代码(或AST)。 因此,宏展开时的上下文就是您调用/使用它的地方。 因此,如果使用宏(未定义) SomeOtherModule
,则它是宏扩展的上下文。 在SomeOtherModule
您需要import MacroFun
才能在本地调用其功能。
下面的代码在Elixir> = 1.2.0中有效
例如
defmodule MacroFun do
defmacro defkv(kv) do
_defkv(kv)
end
defp _defkv(kv) do
quote bind_quoted: [kv: kv] do
Enum.each kv, fn {k, v} ->
def unquote(k)(), do: unquote(v)
end
end
end
defmacro def_modified_kv(kv) do
quote bind_quoted: [kv: kv] do
modified_kv = Enum.map kv, fn {k, v} -> {k, v + 1} end
defkv(modified_kv)
end
end
end
defmodule Runner do
import MacroFun
kv = [foo: 1, bar: 2]
def_modified_kv(kv)
end
希望这能回答您的问题! 祝好运。
更新了两次,因为宏有些混乱!
1)通过使用require
,您已经告诉编译器编译并提供MacroFun
的公共宏和函数。 请注意,这些功能仍然可以使用:
请注意,通常在使用前不需要模块,唯一的例外是如果要使用模块中的宏。
2)可通过FQN获得
3)这是我的看法:
defmodule MicroFun do
defmacro defkv(kv) do
_defkv(kv)
end
defp _defkv(kv) do
quote bind_quoted: [kv: kv] do
Enum.each kv, fn {k,v} ->
def unquote(k)(), do: unquote(v)
end
end
end
defmacro modifier(kv) do
quote bind_quoted: [kv: kv] do
Enum.map kv, fn {k,v} -> {k, v+1} end
end
end
end
defmodule Runner do
require MicroFun
[foo: 1, bar: 2]
|> MicroFun.modifier
|> MicroFun.defkv
end
这个想法是,当您可以通过管道传递宏/函数时,就不需要嵌套它们。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.