Нет способа вывести точный номер строки этого определения определения "print_block" без метапрограммирования в Elixir? - PullRequest
0 голосов
/ 21 января 2019

Я работаю с Elixir ... Я обнаружил, что при определении приведенного ниже кода без макросов мне нужно поставить inspect __ENV__ и вычесть 2 из напечатанного номера строки, чтобы получить номер строки, которую запускает print_blockon.

h1 do
  h2 do
    print_block "prints message" do
      IO.puts "hello world!"
      IO.inspect(__ENV__)
    end
  end
end

Определение функции print_block выглядит примерно так:

def print_block(_msg, do: code_block), do: code_block

Аналогичный код определяет блоки h1 и h2.

Теперь, если я вместо этого определю h1, h2 и print_block в качестве макросов, я могу получить точный номер строки print_block в существующем файле, пройдя AST, полученный при проверке code_block.

defmacro print_block(_msg, do: code_block) do
  IO.inspect(code_block)
  code_block
end

Поскольку метапрограммирование Elixir позволяет вам исследовать AST, единственный способ точно сопоставить номер строки print_block, не прибегая к «вычитанию некоторого числа», - это определить его с помощью макросов.Вы не можете сделать это с традиционными модулями / функциями времени выполнения, это правильно?Уверен, что это так, просто жду подтверждения.

1 Ответ

0 голосов
/ 22 января 2019

Поскольку метапрограммирование Elixir позволяет вам исследовать AST [...]

Это не совсем так.Что делает макрос, он буквально вставляет AST вместо вызова до того, как произойдет компиляция .В скомпилированном коде нет следов каких-либо макросов, все они пропали, их заменили на AST, которые они возвращали на этапе компиляции.

Это означает, что ваш макрос не работает так, как вы ожидаете.IO.inspect выполняется на этапе компиляции .ЛУЧ не будет распечатывать ничего, равно как и __ENV__.line или что-либо подобное.

OTOH, Kernel.SpecialForms.quote/2 имеет специально выделенную опцию location: :keep для сохранения файла.и номер строки.Это не произойдет автоматически из коробки.

Кроме того, есть атрибут модуля @compile, который можно вставить в модули, чтобы указать, какие функции должны быть встроены;в основном, inlining делает почти то же самое, что и вызов макроса.


Я не смог понять, к какой именно цели вы стремитесь, но предположение, что макрос мог бы волшебным образом помочь засечь точный номер строки, совершенно неверно..


Вот демонстрация всех возможностей получить строку из __ENV__:

bat /tmp/foo.ex
───────┬───────────────────────────────────────────────────────────────────
       │ File: /tmp/foo.ex
───────┼───────────────────────────────────────────────────────────────────
   1   │ defmodule Foo do
   2   │   @compile {:inline, print_block_inline: 2}
   3   │ 
   4   │   def print_block_inline(_msg, do: code_block), do: code_block
   5   │   def print_block_normal(_msg, do: code_block), do: code_block
   6   │   defmacro print_block_macro(_msg, do: code_block), do: code_block
   7   │ 
   8   │   def test do
   9   │     print_block_normal "prints message" do
  10   │       IO.puts("hello world!")
  11   │       IO.inspect(__ENV__.line, label: "11")
  12   │     end
  13   │ 
  14   │     print_block_inline "prints message" do
  15   │       IO.puts("hello world!")
  16   │       IO.inspect(__ENV__.line, label: "16")
  17   │     end
  18   │ 
  19   │     print_block_macro "prints message" do
  20   │       IO.puts("hello world!")
  21   │       IO.inspect(__ENV__.line, label: "21")
  22   │     end
  23   │   end
  24   │ end
  25   │ 
  26   │ Foo.test()

Все успешно напечатано.

elixir /tmp/foo.ex

hello world!
11: 11
hello world!
16: 16
hello world!
21: 21
...