12
votes

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.

2

2 Answers

12
votes

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
9
votes

Try this trick from Chris McCord:

your_ast |> Macro.expand(__ENV__) |> Macro.to_string |> IO.puts