Elixir 1.3.0-rc1 编译器无法编译我的宏之一。在 Elixir 1.2.6 中还可以。
defmodule M do
defmacro ifa(a, exp) do
if (Macro.expand_once(a, __ENV__)), do: exp
end
end
defmodule Foo do
@flag true
require M
def main do
M.ifa (@flag), do: IO.puts 123
end
end
Foo.main
Run Code Online (Sandbox Code Playgroud)
编译器抱怨该属性。
% /tmp/elixir-1.3.0-rc1/bin/elixir foobar.exs
** (ArgumentError) could not call get_attribute on module M because it was already compiled
(elixir) lib/module.ex:1144: Module.assert_not_compiled!/2
(elixir) lib/module.ex:1066: Module.get_attribute/3
(elixir) lib/kernel.ex:2360: Kernel.do_at/4
(elixir) expanding macro: Kernel.@/1
foobar.exs:12: M.ifa/2
expanding macro: M.ifa/2
foobar.exs:12: Foo.main/0
% /tmp/elixir-1.2.6/bin/elixir foobar.exs
123
Run Code Online (Sandbox Code Playgroud)
我想知道为什么 Foo 在扩展宏之前被编译。1.3 改变了什么?
Elixir 实际上在您的代码中发现了一个错误!:D
在宏中,当您使用 时__ENV__,您将在定义宏的模块上下文中而不是在调用者上下文中扩展用户引用的表达式。解决方案是使用Elixir v1.2 和 v1.3 中的 's context__CALLER__来确保@flag正确扩展:Foo
defmodule M do
defmacro ifa(a, exp) do
if (Macro.expand_once(a, __CALLER__)), do: exp
end
end
Run Code Online (Sandbox Code Playgroud)
感谢您尝试 Elixir v1.3-rc!