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