Elixir has been my goto language for the past 18 months or so, however I sometimes find there is a tension between the "no magic" mantra (especially cited with reference to Phoenix vs Rails) and the use of macros.
While I now miss macros when I'm using languages without them, I still wish it was easier to see what they are actually doing. Some part of me always wants to pull back the DSL curtain and see the real code.
Is there a simple way to expand macros and see the code they generate, (perhaps via IEx) so that I don't have to dig through the layers of defmacro trying to piece it together in my head.
Macros are compile-time constructs that are invoked with Elixir's AST as input and a superset of Elixir's AST as output. The macro receives the representation of the code given as argument, while a function receives the result of the code given as argument. A macro must return a superset of the code representation.
Elixir already provides mechanisms to write your everyday code in a simple and readable fashion by using its data structures and functions. Macros should only be used as a last resort. Remember that explicit is better than implicit. Clear code is better than concise code.
So What Is 'use'? here's a lightweight explanation of 'use' in elixir as I currently understand it. Use is a tool to reuse code just like require, import, and alias. Use simply calls the __using__ macro defined in another module. The __using__ macro allows you to inject code into another module.
You can expand a macro with Macro.expand/2
iex> Macro.expand((quote do: (if true, do: 1)), __ENV__)
{:case, [optimize_boolean: true],
[true,
[do: [{:->, [],
[[{:when, [],
[{:x, [counter: 6], Kernel},
{:in, [context: Kernel, import: Kernel],
[{:x, [counter: 6], Kernel}, [false, nil]]}]}], nil]},
{:->, [], [[{:_, [], Kernel}], 1]}]]]}
You can then use Macro.to_string/2 to get the output as a string instead of an AST:
iex> Macro.expand((quote do: (if true, do: 1)), __ENV__) |> Macro.to_string()
"case(true) do\n x when x in [false, nil] ->\n nil\n _ ->\n 1\nend"
You can then use IO.puts/2 to print the string to the terminal:
iex> Macro.expand((quote do: (if true, do: 1)), __ENV__) |> Macro.to_string() |> IO.puts()
case(true) do
x when x in [false, nil] ->
nil
_ ->
1
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With