elixir macros that use module attributes
A macro I was writing required some default values for a using module file. The apparent way of doing this is to pass options to use that get turned into module attributes. I encountered some problems while trying to make this work and thought this might help explain.
The few things of most importance are:
- Module attributes are a compile time only thing
- Macros are evaluated at compile time, producing code which is then compiled
- Passing a value to a macro is not the same as passing a module attribute
On that last point, passing a literal @att to a macro will provide the macro with an AST that is a call to Module.get_attribute, which is typically not what you want if you're interested in the compile time value of that attribute.
The attribute in my case is the formation of a contract between the __using__ set up and the macro's running. It's better for the world outside the macro module to not see or know about this attribute.
The code:
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
The attribute wrapper_fields is created during __using__ and then used in the result_wrapper macro to make a complete list.
The non-working and discussion of this problem can be found on ElixirForum