Eixir元编程 - 在宏内编译时定义宏

时间:2017-02-15 02:01:36

标签: elixir metaprogramming

我正在研究Elixir元编程,我正在努力创建一个允许我定义REST资源的macro。界面将是这样的:

defmodule Router do
  use Resources

  resource "cars"
  resource "animals"
end

我使用Module模块定义模块属性,但我无法使用以下内容:

defmodule Resource do
  defmacro __using__(_opts) do
    quote do
      Module.put_attribute __MODULE__, :stack, [1, 2, 3]
      defmacro resource(name) do
        stack = Module.get_attribute __MODULE__, :stack
        Module.put_attribute __MODULE__, :stack, [name|stack]
      end
    end
  end
end

以下内容无法编译:

defmodule Domain  do
  use Resource

  resource "foo"

  def run do
    IO.inspect @stack
  end
end

如果删除资源行,则会正确打印[1, 2, 3]

resource/1可以看到run/0宏。

我怎样才能让Router中的代码工作,以便调用资源“xxx”将“xxx”推送到@stack模块属性的堆栈?

1 个答案:

答案 0 :(得分:1)

您需要单独定义第二个Macro,而不是__using__宏。您可以将第一个宏用于import资源并定义初始@stack,以便您可以在模块中使用resource宏。

您也不需要致电Module.get_attributeModule.put_attribute,只需在任何地方使用@stack即可。

试试这个:

defmodule Resource do
  defmacro __using__(_opts) do
    quote do
      import Resource
      @stack [1,2,3]
    end
  end

  defmacro resource(name) do
    quote do
      @stack [unquote(name) | @stack]
    end
  end
end

现在调用Domain.run应该会为您["foo", 1, 2, 3]。您还应该阅读关于在Elixir中建立自己的Domain Specific Languages的官方指南。