I’d like to understand macro expansion rules, so I created this piece of code:
lib/my_macro.ex:
defmodule MyMacro do
defmacro print_module_attribute(attribute) do
IO.inspect "ATTRIBUTE:"
IO.inspect attribute
IO.inspect "EXPANDED VALUE"
IO.inspect Macro.expand attribute, __CALLER__
quote do
nil
end
end
end
lib/main.ex
defmodule Main do
require MyMacro
@my_attr :asdf
MyMacro.print_module_attribute(@my_attr)
def in_function do
MyMacro.print_module_attribute(@my_attr)
end
end
When running mix compile
, I get the following output:
Compiling 2 files (.ex)
"ATTRIBUTE:"
{:@, [line: 4], [{:my_attr, [line: 4], nil}]}
"EXPANDED VALUE"
{:with, [],
[{:<-, [],
[{:when, [],
[{{:_, [], Kernel}, {:doc, [counter: -576460752303423485], Kernel}},
false]},
{{:., [],
[{:__aliases__, [alias: false, counter: -576460752303423485], [:Module]},
:get_attribute]}, [],
[{:__MODULE__, [counter: -576460752303423485], Kernel}, :my_attr,
[{:{}, [], [Main, :__MODULE__, 0, [file: "lib/main.ex", line: 4]]}]]}]},
[do: {:doc, [counter: -576460752303423485], Kernel}]]}
"ATTRIBUTE:"
{:@, [line: 6], [{:my_attr, [line: 6], nil}]}
"EXPANDED VALUE"
:asdf
Generated macro_problem app
It is a little bit surprising to me. When I call the macro in the module scope, it can’t expand the module attribute but when I call it from the function scope it can.
I wonder why is that? I am guessing that there are multiple expansion phases and compiler firstly expands the macro in module scope, secondly module attributes and thirdly function definitions.
However, defmodule
and def
are macros themselves so I am not sure if my theory makes sense.
Could you point me to a piece of documentation that explains it in details?