因此在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
是完全扩展的宏代码的快捷方式?
答案 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中执行的代码而存在的(通过编译后。)宏不。