If I pass @attr to a macro, the macro receives an AST of Module.get_attribute. How can the macro get the actual value?
The macro intends to combine the list value of an attr with another argument to the macro and use the resulting list in an Enum.map to create quoted code.
Since you’re operating on a module attribute you don’t really need to pass it in to the macro. Something as simple as this might help demonstrate:
defmodule M do
@attr [1,2,3]
defmacro attr do
quote do
Enum.each @attr, fn x ->
IO.puts x
end
end
end
def test_attr do
attr()
end
end
Although it would be somewhat more expressive to the following:
defmodule M do
@attr [1,2,3]
defmacro attr do
quote bind_quoted: [attr: @attr] do
Enum.each attr, fn x ->
IO.puts x
end
end
end
def test_attr do
attr()
end
end
If you want to use a module attribute from another module that is being compiled then something like this might help:
defmodule M do
@attr [1,2,3]
defmacro attr do
# In this case get the attribute from the calling module
attribute =
Module.get_attribute(__CALLER__.module, :attr)
quote bind_quoted: [attr: attribute] do
attr
end
end
def test_attr do
IO.inspect attr()
end
end
defmodule ResultWrapper do
defmacro result_wrapper( wrapper_name, component_keyword_list, common ) do
cx = component_keyword_list ++ [ do something to common to make it a value ]
fields =
Enum.map( cx, fn { k, t } -> quote do
field unquote( k ), unquote( t )
end
end )
quote location: :keep do
object unquote( wrapper_name ) do
unquote( fields )
end
end
end
defmacro __using__( wrapper_fields ) do
quote do
import ResultWrapper
@wrapper_common unquote( wrapper_fields )
end
end
end
I had a few subtle things wrong. It was always my intention for a using call to set up a module attr that later macro calls would use. I needed to put the Module.put_attribute in the unquoted section of the using and not try to declare the @ in the quoted section (the value was not appearing otherwise). Then in the macro, call get_attribute. The other key bit is using CALLER.module (thank you @kip), I wasn’t sure where the module was hiding.
This works:
defmodule ResultWrapper do
defmacro result_wrapper( wrapper_name, component_keyword_list ) do
cx = component_keyword_list ++
Module.get_attribute( __CALLER__.module, :wrapper_common )
fields =
Enum.map( cx, fn { k, t } -> quote do
field unquote( k ), unquote( t )
end
end )
quote location: :keep do
object unquote( wrapper_name ) do
unquote( fields )
end
end
end
defmacro __using__( wrapper_fields ) do
Module.put_attribute( __CALLER__.module, :wrapper_common, wrapper_fields )
quote do
import ResultWrapper
end
end
end