首页 > 解决方案 > 在宏中定义和使用函数

问题描述

按照这个例子:

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/

标签: macroselixir

解决方案


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

为此,您需要在与make 可用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

推荐阅读