Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

macros

elixir

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.

like image 649
Foo42 Avatar asked Feb 04 '16 15:02

Foo42


People also ask

How will you explain macros in Elixir?

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.

When should I use macros in Elixir?

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.

What is use Elixir?

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.


1 Answers

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
like image 64
Gazler Avatar answered Sep 20 '22 17:09

Gazler