【问题标题】:Macros and module attributes in Elixir 1.3Elixir 1.3 中的宏和模块属性
【发布时间】:2016-06-14 07:10:12
【问题描述】:

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

编译器抱怨该属性。

% /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

我想知道为什么在扩展宏之前编译 Foo。 1.3有什么变化?

【问题讨论】:

  • 我可以在 1.2.6 和 1.3 分支上的最新提交中重现相同的行为。如果您在这里没有得到答案,我建议您在 IRC 频道(freenode 上的#elixir-lang)或官方 Google 群组上提问。这可能是一个错误,因为它破坏了在 1.2 中运行良好的代码。

标签: macros elixir


【解决方案1】:

Elixir 实际上在您的代码中发现了一个错误! :D

在您的宏中,当您使用__ENV__ 时,您将在定义宏的模块的上下文中而不是在调用者的上下文中扩展用户引用的表达式。解决方案是使用__CALLER__ 确保@flag 在Elixir v1.2 和v1.3 中使用Foo 的上下文得到正确扩展:

defmodule M do
  defmacro ifa(a, exp) do
    if (Macro.expand_once(a, __CALLER__)), do: exp
  end
end

感谢您试用 Elixir v1.3-rc!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 2016-06-24
    • 1970-01-01
    • 2016-04-25
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多