Reputation: 8998
Following this example:
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
Can it be possible to define internal functions to use within the macro?
For example:
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
Trying this the compiler complais with function say_grettings is not defined
That example source http://elixir-recipes.github.io/metaprogramming/accumulating-annotations/
Upvotes: 0
Views: 2226
Reputation: 121010
Well, this is possible. The issue with your code is that you mix the scopes. Macros in Elixir are being expanded during the compilation stage. There is no compiled say_greetings/1
function at this very moment (besides that one cannot invoke defp
from inside defmacro
, but this might be overcame with a proper quoting.)
What you need for this to work, would be to declare say_greetings/1
in the same scope as __before_compile__/1
to make is available for it. You cannot define it as a function (see above,) but the workaround would be to define it as a macro as well. That way it will be expanded during a compilation and everything will work (also, I doubt I understand what is the reason for that.)
The summing up:
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
Upvotes: 2