在编译阶段,我可以轻松地生成函数:
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
现在我的问题是:如何使用动态创建的函数名称动态生成模块 ,e。 G:
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
NB 我能够实现我想要的目标
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
但似乎非常黑客。
答案 0 :(得分:2)
我认为这是由于嵌套的宏调用(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