Анализ макроса эликсира после раскрытия и его определения - PullRequest
0 голосов
/ 01 февраля 2019

Таким образом, в документах для defmacro в Elixir у нас есть (я добавляю метод hello в качестве примера):

defmodule MyLogic do
  defmacro unless(expr, opts) do
    quote do
      if !unquote(expr), unquote(opts)
    end
  end

  def hello, do: "hello"
end

Тогда, если я попытаюсь проанализировать доступные функции, только привет/ 0 показывает.

iex(3)> MyLogic.__info__(:functions)
[hello: 0]

Но вызов:

MyLogic.unless false do
  IO.puts("It works")
end

вызывает макрос.

Насколько я понимаю, макрос развернут в окончательную форму AST, и нет следовэто когда-то это скомпилировано.Что меня несколько смущает, так это то, что кажется, что вы можете вызывать unless как функцию, но ее нет во внутреннем анализе .__info__(:functions).

1) Я думаю, что вы не можете провести самоанализ по определениям макросовпосле компиляции, потому что после компиляции ее не осталось и следа, но при построении ментальной модели «разве что» после ее расширения, как мне представить это в своем уме?Должен ли я просто посмотреть на это, поскольку «MyLogic» теперь имеет имя этого атома unless, которое вызывает результат выражения if if !unquote(expr), unquote(opts).
2) И как же Elixir узнает, что unless является ярлыком дляполностью развернутый код макроса?

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

@ TGO уже дал идеальный ответ, почему __info__(:functions) не показывает unless;Я все еще думаю, что требуется какое-то разъяснение.


Насколько я понимаю, макрос развернут в окончательную форму AST и его не остается после его компиляции.

Это действительно очень верно. Дело в том, что Elixir компилирует все перед выполнением, и, записывая MyLogic.unless ..., вы никоим образом не вызываете / вызываете макрос.Что происходит, вы приказываете Elixir вводить AST, возвращаемый MyLogic.unless в ваш код вместо MyLogic.unless, во время компиляции вашего кода .В полученном ЛУЧЕ нет никаких следов макроса.

как мне представить его в моем уме?

MyLogic.unless false, do: IO.puts("It works")

расширен точно до:

if !false, do: IO.puts("It works")

unquote необходимо в объявлении макроса при вызове quote do, поскольку макросы получают выражений в кавычках в качестве аргументов.Вы можете попытаться IO.inspect/2 аргументы внутри вызова макроса , вне quote do:

defmacro unless(expr, opts) do
  IO.inspect({expr, opts}, label: "compilation time!")
  quote, do: if !unquote(expr), unquote(opts)
end

, как Elixir узнает, что unless являетсяярлык к полностью расширенному макрокоду?

Есть действительно замечательное объяснение того, как Elixir поддерживает внутреннюю информацию на этапе компиляции, которую это поле слишком мало, чтобы содержать.В общем, эликсир ничего не знает .Вы должны четко различать время компиляции и время выполнения контексты.Вы говорите о казни .Это происходит после того, как все скомпилировано и ваш пользовательский MyLogic.unless/1 больше не существует, заменяется введенным AST.

Elixir не может выполнить не скомпилированный код, это не язык сценариев.Не дайте себя одурачить REPL: iex под капотом эффективно компилирует все , которое вы «выполняете» там до реального выполнения.

, что макросв Elixir на самом деле есть функция (из ваших комментариев к другому ответу)

Это ни в коем случае не правильно.Функции существуют как код, который выполняется ErlangVM в полученном BEAM (после прохождения компиляции.) Макросы не .

0 голосов
/ 01 февраля 2019

Согласно документации , атом :functions используется для извлечения функций только модуля.

Если вы хотите получить макросы вместо этого, вы должны указать атом:macros до __info__/1.

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

...