在宏内定义和使用函数

时间:2018-05-16 11:07:47

标签: macros elixir

遵循这个例子:

defmodule Greetify do

  defmacro __using__(_) do
    quote do
      Module.register_attribute __MODULE__, :greet, accumulate: true,
        persist: false
      @before_compile Greetify
    end
  end

  defmacro __before_compile__(env) do
    greetings = Module.get_attribute(env.module, :greet)
    for {name, age} <- greetings do
      IO.puts "#{name} is #{age} years old"
    end
  end

end

是否可以定义要在宏中使用的内部函数?

例如:

  defmacro __before_compile__(env) do
    greetings = Module.get_attribute(env.module, :greet)
    say_greetings(greetings)

    defp say_greetings(grettings) do
      for {name, age} <- greetings do
        IO.puts "#{name} is #{age} years old"
      end
    end
  end

尝试这个编译器抱怨函数say_grettings没有定义

该示例来源http://elixir-recipes.github.io/metaprogramming/accumulating-annotations/

1 个答案:

答案 0 :(得分:2)

嗯,这是可能的。代码的问题在于您混合了范围。 Elixir中的宏在编译阶段正在扩展。目前没有编译say_greetings/1函数(除此之外,不能从defp内部调用defmacro,但这可能会通过适当的引用克服。)

您需要执行此操作,将say_greetings/1声明为与__before_compile__/1相同的范围,以使其可用。您无法将其定义为函数(参见上文),但解决方法是将定义为宏。这样在编译期间它将扩展并且一切都会起作用(同样,我怀疑我理解这是什么原因。)

总结:

defmodule Greetify do    
  defmacro __using__(_) do
    quote do
      Module.register_attribute __MODULE__, :greet, accumulate: true, persist: false
      @before_compile Greetify
    end
  end

  defmacrop say_greetings(greetings) do
    quote do
      for {name, age} <- unquote(greetings) do
        IO.puts "#{name} is #{age} years old"
      end
    end
  end

  defmacro __before_compile__(env) do
    greetings = Module.get_attribute(env.module, :greet)
    say_greetings(greetings)
  end
end

defmodule Test do
  use Greetify

  @greet {"Jon", 21}
  @greet {"Sam", 23}
end