扩展后自省e剂宏及其定义

时间:2019-02-01 16:45:37

标签: elixir

因此在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是完全扩展的宏代码的快捷方式?

2 个答案:

答案 0 :(得分:2)

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

如果要获取宏,则应将原子:macros提供给__info__/1

由于您不能同时拥有相同的名称和别名的宏和函数,因此Elixir可以轻松区分两者。

答案 1 :(得分:0)

@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")
unquote调用下的宏声明中必须有

quote do,因为宏确实会接收用引用的表达式作为参数。您可以尝试在IO.inspect/2之外的宏调用内quote do {em>

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可以在 real 执行之前有效地编译您在其中“执行”的所有内容

  

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

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