Let's say I have a modules Silent
and Definer
. I want to define a couple of functions for Silent
, based on its attribute. Let me explain:
defmodule Silent do
@function_names [:a, :b, :c]
use Definer
end
defmodule Definer do
defmacro __using__(_) do
quote do
Enum.each(@function_names, fn(n) ->
def unquote(n)() do # line 5
IO.puts "a new method is here!"
end
end)
end
end
end
But this approach actually doesn't work because I have undefined function n/0 on line 5
. How can I implement desired functionality?
You need to pass unquote: false
to quote
in Definer.__using__/1
to be able to inject an unquote
fragment inside a quote
.
defmodule Definer do
defmacro __using__(_) do
quote unquote: false do
Enum.each(@function_names, fn(n) ->
def unquote(n)() do # line 5
IO.puts "a new method is here!"
end
end)
end
end
end
defmodule Silent do
@function_names [:a, :b, :c]
use Definer
end
Silent.a
Silent.b
Silent.c
prints
a new method is here!
a new method is here!
a new method is here!
A similar case is documented in detail in the Kernel.SpecialForms.quote/2 docs which also mentions how to use bind_quoted
if you want to both inject some variables into a quote
and create unquote
fragments.