macros - 如何动态创建具有功能的模块

标签 macros elixir metaprogramming

在编译阶段,我可以轻松地生成函数:

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。

最佳答案

我相信这是由于嵌套的宏调用而发生的(defdefmodule 都是宏)。如果您在此处放置一个 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/

相关文章:

c - C宏中参数的单展开

JavaScript + ANT 问题

c - (C99)在不同的宏中扩展一个宏

elixir - 如何使用加载的关联序列化/反序列化 Ecto 模型?

c++ - 在编译时将字符串转换为大写

serialization - Julia:在运行时生成代码并将其存储以供将来评估/执行

c - 如何使用以下预处理器语句

elixir - 如何使用phoenix对两种参数进行模式匹配

erlang - 你如何处理 Elixir 中的 base64 编码文件?

c++ - std::is_same 无法通过 constexpr 自动变量的 decltype 工作