距离写上一篇文章已经过去了将近1年时间。
使用elixir进行元编程的时候,初学者经常会不清楚模块的编译顺序。我们用一个简单的例子来解释一下。
defmodule M1 do
@after_compile M0
@before_compile M0
use M0
@a IO.inspect("compiling module attribute a")
end
我们定义了一个 M1 模块,它使用了几个 elixir 内置的模块属性:
@after_compile M0
表示在 M1 模块编译之后,调用M0.__after_compile__
@before_compile M0
表示在 M1 模块编译之前,调用M0.__before_compile__
接着我们定义 M0
模块, 在每个hook被调用时打印相关的信息:
defmodule M0 do
defmacro __using__(_) do
mod = __CALLER__.module
Module.register_attribute mod, :a, accumulate: true
IO.puts "#{__MODULE__} being used by #{mod}"
end
defmacro __after_compile__(_, _) do
IO.puts "#{__MODULE__} after compile be called"
end
defmacro __before_compile__(_) do
IO.puts "#{__MODULE__} before compile be called"
Module.get_attribute(__CALLER__.module, :a)
|> IO.inspect(label: "#{__CALLER__.module} has attribute a")
end
end
惊人的一幕发生了,我们看到了这样的顺序:
Elixir.M0 being used by Elixir.M1
"compiling module attribute a"
Elixir.M0 before compile be called
Elixir.M1 has attribute a: ["compiling module attribute a"]
Elixir.M0 after compile be called
结论是,在 elixir 编译器编译一个模块的时候,hooks 按照这样的顺序被调用的:
__using__
- module attributes 被编译
__before_compile__
__after_compile__
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。