[英]Double unquoting for dynamically generated macros
給定以下內容:
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
fn_name = unquote(fn_name)
的原因是什么? 如果我省略此行,則為編譯錯誤。 這種“雙重”取消報價的原因是什么?
讓我們稍微簡化一下示例:
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
在上面的示例中,因為quote返回帶有兩個未引用元素的元組,所以它等效於:
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
現在,更容易理解如果您之前不unquote(fn_name)
會發生什么:宏定義內根本就不存在變量fn_name
。 請記住,所有def
(def,defp,defmacro等)都會啟動一個新的變量作用域,因此,如果要在內部使用fn_name,則需要以某種方式對其進行定義。
我們在此代碼中看到的另一個屬性是Elixir在看到quote
時將停止取消quote
。 因此在上面的報價, unquote
的宏定義,而是在執行宏,這也解釋了為什么需要變量的宏內部被定義時,不會加引號。
這是因為衛生 。
長生不老葯有宏觀衛生學的概念。 衛生意味着您在宏中定義的變量,導入和別名不會泄漏到調用者自己的定義中。
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
您應該從Chris McCord的Metaprogramming Elixir書中閱讀“ 衛生保護呼叫者的上下文 ”
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.