如果没有在Elixir中进行元编程,没有办法推断此“ print_block”定义执行的确切行号吗?

时间:2019-01-21 20:28:50

标签: elixir

我正在与Elixir一起工作...我发现,在定义以下不带宏的代码时,我不得不放置inspect __ENV__并从打印的行号中减去2以得到{{1} }正在运行。

print_block

print_block的函数定义如下:

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

类似的代码定义了h1和h2块。

现在,如果我改为将def print_block(_msg, do: code_block), do: code_block h1h2定义为宏,则可以通过以下方式在现有文件中获取print_block的确切行号:我检查print_block时产生的AST。

code_block

因为Elixir元编程使您可以检查AST,所以唯一地精确匹配print_block的行号而不求助于“减去某些数字”的唯一方法是通过宏对其进行定义。使用传统的运行时模块/功能无法做到这一点,对吗?可以肯定的是,只是想确认一下。

1 个答案:

答案 0 :(得分:1)

  

因为Elixir元编程可以让您检查AST [...]

情况并非完全如此。宏的作用是,在编译发生之前,它将注入 AST代替调用。编译后的代码没有任何宏的痕迹,它们都消失了,由在编译阶段返回的AST代替。

这意味着您的宏无法按预期工作。 IO.inspect在编译阶段执行。 BEAM不会打印任何内容,也不会有__ENV__.line或类似内容。

OTOH,Kernel.SpecialForms.quote/2具有专门用于保存文件和行号的location: :keep选项。开箱即用不会自动发生。

此外,还有一个@compile模块属性,可以将其放入模块中以指定应内联哪些函数;基本上内联与调用宏几乎一样。


我无法理解您要追求的确切目标是什么,但是假设宏可以神奇地帮助您确定确切的行号是完全错误的。


以下是从__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