Есть ли простой способ увидеть, к чему расширяется макрос эликсиров?

Elixir был моим языком goto в течение последних 18 месяцев или около того, однако иногда я нахожу, что существует напряженность между мантрами "no magic" (особенно цитируется со ссылкой на Phoenix vs Rails) и использованием макросов.

Пока я пропускаю макросы, когда я использую языки без них, я все же хочу, чтобы было легче увидеть, что они на самом деле делают. Некоторая часть меня всегда хочет оттянуть занавес DSL и увидеть реальный код.

Есть ли простой способ расширения макросов и просмотра кода, который они генерируют (возможно, через IEx), так что мне не нужно прорывать слои defmacro, пытаясь соединить его вместе в моей голове.

Ответ 1

Вы можете развернуть макрос с помощью 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]}]]]}

Затем вы можете использовать Macro.to_string/2, чтобы получить результат как строку вместо 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"

Затем вы можете использовать IO.puts/2 для печати строки на терминале:

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

Ответ 2

Попробуйте этот трюк у Криса МакКорда:

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