在编译阶段,我可以轻松地生成函数:
defmodule A1 do
defmodule A2 do
Enum.each %{m: 42}, fn {k, v} ->
def unquote(k)(), do: unquote(v)
end
end
end
IO.puts A1.A2.m
#⇒ 42
另外,我可以在函数调用中生成带有函数的模块:
defmodule B1 do
def b2! do
defmodule B2 do
# enum is for the sake of future example
Enum.each %{m1: 42}, fn {_k, v} ->
# def b2(), do: unquote(v) WON’T WORK (WHY?), BUT
@v v
def b2(), do: @v
end
end
end
end
B1.b2! # produce a nested module
IO.puts B1.B2.b2 # call a method
#⇒ 42
现在我的问题是:如何使用动态创建的函数名称动态生成模块,例如。例如:
defmodule B1 do
def b2! do
defmodule B2 do
Enum.each %{m1: 42, m2: 3.14}, fn {k, v} ->
@k k
@v v
def unquote(@k)(), do: @v # THIS DOESN’T WORK
end
end
end
end
注意我能够实现我想要的
defmodule B1 do
def b2! do
defmodule B2 do
Enum.each %{m1: 42, m2: 3.14}, fn {k, v} ->
ast = quote do: def unquote(k)(), do: unquote(v)
Code.eval_quoted(ast, [k: k, v: v], __ENV__)
end
end
end
end
但它似乎很hacky。
最佳答案
我相信这是由于嵌套的宏调用而发生的(def
和 defmodule
都是宏)。如果您在此处放置一个 unquote
,它会从顶级 def
中取消引用:
defmodule B1 do
k = :foo
v = :bar
def b2! do
defmodule B2 do
def unquote(k)(), do: unquote(v)
end
end
end
B1.b2!
IO.inspect B1.B2.foo
打印
:bar
Module.create/3
建议在正文是 AST 时使用该函数动态创建模块。有了它,代码变得比使用 Code.eval_quoted/3
的 hacky 解决方案优雅得多:
defmodule B1 do
def b2! do
ast = for {k, v} <- %{m1: 42, m2: 3.14} do
quote do
def unquote(k)(), do: unquote(v)
end
end
Module.create(B1.B2, ast, Macro.Env.location(__ENV__))
end
end
B1.b2!
IO.inspect B1.B2.m1
IO.inspect B1.B2.m2
输出:
42
3.14
关于macros - 如何动态创建具有功能的模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44623966/