Elixir 1.3中的宏和模块属性
问题描述:
Elixir 1.3.0-rc1编译器无法编译我的一个宏。 在Elixir 1.2.6中没问题。Elixir 1.3中的宏和模块属性
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
编译器抱怨属性。
% /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: [email protected]/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
我想知道为什么Foo在扩展宏之前编译。 1.3有什么变化?
答
Elixir在您的代码中实际发现了一个错误! :D
在您的宏中,当您使用__ENV__
时,您将在定义宏的模块的上下文中扩展用户引用的表达式,而不是在调用方的上下文中。该解决方案是使用__CALLER__
确保@flag
两种药剂V1.2和V1.3使用Foo
的情况下被适当扩大:
defmodule M do
defmacro ifa(a, exp) do
if (Macro.expand_once(a, __CALLER__)), do: exp
end
end
感谢您尝试药剂V1.3-RC!
从现在开始,我可以用1.2.6和1.3分支上的最新提交重现相同的行为。如果您在这里没有得到答案,我会建议在IRC频道(freenode上的#elixir-lang)或官方Google Group上提问。这可能是一个错误,因为它破坏了在1.2中正常工作的代码。 – Dogbert