Elixir 1.3 中的宏和模块属性

moc*_*cos 1 macros elixir

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 改变了什么?

Jos*_*lim 5

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!