Reputation: 9691
Given the following:
for fn_name <- [:foo, :bar, :baz] do
defmacro unquote(fn_name)(do: inner) do
fn_name = unquote(fn_name) # <--- Why?
quote do
IO.puts "#{unquote(fn_name)} called"
unquote(inner)
end
end
end
What's the reason for fn_name = unquote(fn_name)
? If I omit this line, it's a compile error. What's the reason for this "double" unquoting?
Upvotes: 1
Views: 166
Reputation: 51369
Let's simplify the example a little bit:
for fn_name <- [:foo, :bar, :baz] do
defmacro unquote(fn_name)(do: inner) do
fn_name = unquote(fn_name) # <--- Why?
quote do
{unquote(fn_name), unquote(inner)}
end
end
end
In the example above, because quote is returning a tuple with two unquoted elements, it is equivalent to:
for fn_name <- [:foo, :bar, :baz] do
defmacro unquote(fn_name)(do: inner) do
fn_name = unquote(fn_name) # <--- Why?
{fn_name, inner}
end
end
Now it is easier to understand what happens if you don't unquote(fn_name)
before: the variable fn_name
simply wouldn't exist inside the macro definition. Remember that all def
s (def, defp, defmacro, etc) start a new variable scope, so if you want to use fn_name inside, you need to define it somehow.
The other property we are seeing in this code is that Elixir will stop unquoting when it sees a quote
. So in the quote above, unquote
won't be unquoted when the macro is defined but rather when the macro is executed, which also explains why the variable is required to be defined inside the macro.
Upvotes: 8
Reputation: 2813
It's because of Hygiene.
Elixir has the concept of macro hygiene. Hygiene means that variables, imports, and aliases that you define in a macro do not leak into the caller’s own definitions.
for fn_name <- [:foo, :bar, :baz] do
defmacro unquote(fn_name)(do: inner) do
fn_name = unquote(fn_name) # <-- This is macro's context
quote do
IO.puts "#{unquote(fn_name)} called" # <-- This is caller's context
unquote(inner)
end
end
end
You should read Hygiene Protects the Caller’s Context from Chris McCord's Metaprogramming Elixir book
Upvotes: 2