【问题标题】:Introspecting an elixir macro after expansion and how it's defined展开后反思 elixir 宏以及它是如何定义的
【发布时间】:2019-02-01 16:45:36
【问题描述】:

所以在 Elixir 中 defmacro 的文档中,我们有(我输入了一个 hello 方法作为示例):

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

  def hello, do: "hello"
end

然后,如果我尝试反省可用的功能,则只有 hello/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 是完全展开的宏代码的快捷方式?

【问题讨论】:

    标签: elixir


    【解决方案1】:

    根据the documentation:functions 原子仅用于检索模块的功能。

    如果您想获取宏,则应改为将原子:macros 提供给__info__/1

    由于不能有一个宏和一个函数同时具有相同的名称和数量,这使得 Elixir 很容易区分两者。

    【讨论】:

    • 好的,谢谢,我刚刚读到 Elixir 中的宏实际上是一个函数,所以这就解释了如何像调用函数一样调用它...你知道 Elixir 源代码在哪里吗定义了允许宏的功能?
    • here 为起点。顺便说一句,您需要了解 Erlang。还可以尝试在 iex 会话中执行 Kernel.__info__(:macros)。我相信你会发现结果很有趣。
    【解决方案2】:

    @TGO 已经完美回答了为什么__info__(:functions) 不显示unless;我仍然认为需要进行一些澄清。


    我的理解是宏被扩展成最终的AST形式,编译后就没有痕迹了。

    确实如此。问题是 Elixir 在执行之前编译所有内容,并且通过编写 MyLogic.unless ... 你绝不是调用/调用宏。会发生什么,您指示 Elixir 将 MyLogic.unless 返回的 AST 注入到您的代码中,而不是 MyLogic.unless在您的代码编译期间。在生成的 BEAM 中没有宏的痕迹。

    我应该如何在我的脑海中表达它?

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

    被扩展为:

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

    unquotequote 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 如何在编译阶段维护内部信息,这个边距太小而无法包含,有一个非常了不起的解释。一般来说,Elixir 什么都不知道。您应该清楚地区分 编译时间运行时 上下文。您正在谈论执行。这发生在所有内容编译完成后,并且您的自定义 MyLogic.unless/1 不再存在,由注入的 AST 代替。

    Elixir 不能执行未编译的代码,它不是脚本语言。不要被 REPL 愚弄:iex 在后台有效地编译所有您在真正执行之前在那里“执行”的所有内容。

    Elixir 中的宏实际上是一个函数(从您的 cmets 到另一个答案)

    这绝不是正确的。函数确实作为代码存在,由 ErlangVM 在生成的 BEAM 中执行(编译通过后)。宏

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-05
      • 2012-07-22
      • 2010-11-22
      • 2015-02-02
      • 1970-01-01
      • 1970-01-01
      • 2021-09-16
      相关资源
      最近更新 更多