Elixir动态生成mod与宏创建函数,解决上下文的问题

时间:2016-02-01 15:58:14

标签: macros elixir

我想生成一个使用宏创建函数的模块,一切都按预期工作但除了unquote(块)上下文似乎超出预期。 测试代码如下。

    defmodule A do
      defmacro wrap(head,opts \\[],do: block) do
        {name,args} = case head do
          name when is_atom(name) ->
          {name,[]}
          h ->
          Macro.decompose_call h
        end
        quote do
          def unquote(name)(unquote_splicing(args)) do
            IO.inspect __ENV__
            unquote(block)
          end
        end
      end
    end

    defmodule B do
      import A,only: [wrap: 2]
      alias B.C
      wrap test do
        C.test2
      end
    end

    defmodule C do
      def test do
        quote do
          import A,only: [wrap: 2]
          alias B.C
          wrap test do
            IO.inspect __ENV__
            C.test2
          end
        end
      end
    end

    defmodule B.C do
      def test2 do
        "good"
      end
    end
Module.create D,C.test,__ENV__

模块B应与D相同,除了D是动态生成的模块,B是预定义的模块。 当调用B.test时,它正确地将C解析为B.C并返回“good”作为结果。 但是当调用D.test时,它引发了无法找到C的异常((UndefinedFunctionError)未定义函数C.test2 / 0)

有没有人对这个问题的根源有一些见解?任何帮助都会得到真正的赞赏。请事先提供帮助;)

更新

确认为错误并已修复

1 个答案:

答案 0 :(得分:0)

基本上,引用块未正确解析别名B.C。如果你直接在块内引用它,问题就会消失。我不确定为什么会这样。具有相同当前模块名称的别名可能使调用不明确。无论哪种方式,您都可以通过将C模块重写为以下内容来解决此问题:

defmodule C do
  def test do
    quote do
      import A, only: [wrap: 2]
      wrap test do
        IO.inspect __ENV__
        B.C.test2
      end
    end
  end
end

结果如下:

iex(1)> D.test
"good"

** 更新:

此别名问题似乎确实是一个错误。插入alias后,宏打印输出会显示CB.C都列为别名,但它仍然无法正常工作。我还验证了当前的模块名称没有引起冲突问题。