🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir Macro Debugging

Macro debugging is checking what code a macro produces before that code runs. It compares the produced code with the code that was wanted.

Text form shows the tree as code

The tree for 1 + 2 reads back as code text. No line details get in the way.

IO.puts(Macro.to_string(quote do: 1 + 2))

Expansion shows the doubled call

Expanding the call shows the repeated expression. Running the expansion gives 42.

defmodule Twice do
  defmacro double(call) do
    quote do
      unquote(call) + unquote(call)
    end
  end
end

defmodule Caller do
  require Twice
  def expanded_text do
    quoted = quote do: Twice.double(21)
    Macro.to_string(Macro.expand(quoted, __ENV__))
  end
  def expanded_value do
    quoted = quote do: Twice.double(21)
    elem(Code.eval_quoted(Macro.expand(quoted, __ENV__)), 0)
  end
end

IO.puts(Caller.expanded_text())
IO.puts(Caller.expanded_value())

A macro can print its own input

The macro prints its input as code text while it expands. The caller then runs the same input and prints 3.

defmodule Debugger do
  defmacro show(expr) do
    IO.puts("AST: " <> Macro.to_string(expr))
    quote do
      unquote(expr)
    end
  end
end

defmodule Caller do
  require Debugger
  def value, do: Debugger.show(1 + 2)
end

IO.puts(Caller.value())

Example

IO.puts(Macro.to_string(quote do: 1 + 2))

defmodule Twice do
  defmacro double(call) do
    quote do
      unquote(call) + unquote(call)
    end
  end
end

defmodule Caller do
  require Twice
  def expanded_text do
    quoted = quote do: Twice.double(21)
    Macro.to_string(Macro.expand(quoted, __ENV__))
  end
  def expanded_value do
    quoted = quote do: Twice.double(21)
    elem(Code.eval_quoted(Macro.expand(quoted, __ENV__)), 0)
  end
end

IO.puts(Caller.expanded_text())
IO.puts(Caller.expanded_value())

Key points

  • Expansion: expansion is the code a macro produces for a call.
  • Macro.to_string: Macro.to_string turns a tree into readable code text.
  • Macro.expand: Macro.expand produces the expansion of a macro call in a given environment.
  • Print in macro: a macro can print its input while it expands, so the input shows during compilation.
  • Separate module: a macro call for debugging still lives in a caller module that is separate from the macro module.

In this example, the first line shows a tree as code text, and the next lines show a macro expansion and the value that expansion gives.

Note

printing the expansion never runs the expanded code, so a second step must run it to check its value.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Which function turns a syntax tree into readable code text?