Foo42
Foo42

Reputation: 3144

Is there an easy way to see what an Elixir macro expands to?

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.

Upvotes: 13

Views: 3914

Answers (2)

Marko Tunjic
Marko Tunjic

Reputation: 1889

Try this trick from Chris McCord:

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

Upvotes: 10

Gazler
Gazler

Reputation: 84180

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

Upvotes: 13

Related Questions