我正在研究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模块属性的堆栈?
I am studying Elixir metaprogramming and I am toying around with making a macro that allows me to define REST resources. The interface would be like this:
defmodule Router do use Resources resource "cars" resource "animals" endI got as far as defining a module attribute using the Module module, but I can't get the following to work:
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 endThe following does not compile:
defmodule Domain do use Resource resource "foo" def run do IO.inspect @stack end endIf I remove the resource line, it prints [1, 2, 3] correctly.
The resource/1 macro is visible from run/0.
How could I get the code in Router to work, so that calling resource "xxx" pushes "xxx" to the stack on the @stack module attribute?
最满意答案
您需要单独定义第二个Macro ,而不是在__using__宏中。 您可以使用第一个宏import Resource并定义初始@stack ,这样您就可以在Module中使用resource宏。
您也不需要调用Module.get_attribute和Module.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中构建自己的域特定语言的官方指南。
You need to define the second Macro separately, not within the __using__ macro. You can use the first macro to import Resource and define the initial @stack, so you can then use the resource macro in your Module.
You also don't need to call Module.get_attribute and Module.put_attribute, and just use @stack everywhere.
Try this:
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 endCalling Domain.run should now give you ["foo", 1, 2, 3]. You should also go over the Official guide on building your own Domain Specific Languages in Elixir.
更多推荐
发布评论