遵循这个例子:
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/
答案 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