2010-05-28 58 views
4

我對使用clojure宏進行符號捕獲的確切時間有點困惑。假設我有一個從關鍵字定義函數的宏。在這個簡單的例子,使用宏來生成函數(或其他宏)時避免符號捕獲

(defmacro foo [keywd1 keywd2] `(defn ~(symbol (name keywd1)) 
        [~(symbol (name keywd2))] (* 2 ~(symbol (name keywd2))))) 

我請(FOO:條:巴茲),並且該被擴展到(DEFN巴[巴茲](* 2巴茲))。

所以現在的問題 - 這可能導致符號捕獲?如果是這樣,在什麼情況下? 我知道它最好使用gensym(例如bar#)來防止符號捕獲,但在某些情況下(不是很多,但仍然)我想要一個漂亮的宏擴展,沒有自動生成的符號。

獎金問題:如果我們正在考慮創建宏的宏,答案會改變嗎?

回答

4

在您的示例中,符號捕捉不會發生,因爲將可變部分作爲參數提供。所以開發者可以自己選擇名字。

當您的宏引入用戶未指定的新當地人時,會發生符號捕獲。考慮以下內容(真正愚蠢無知只是爲了表明這一點)例如:

(defmacro foo 
    [name & body] 
    `(defn ~name 
    [~'bar] 
    (println ~'bar) 
    [email protected])) 

在這種情況下,bar被捕獲。現在假設用戶有這樣的代碼。

(def bar 5) 
(foo baz (* 2 bar)) 
(baz 7) 

這不會給人什麼期望。因爲全球欄,用戶引用的宏被該宏引入的本地欄所遮蔽。正如你已經說過的:在這種情況下,應該使用bar#來引入本地。

所以捕獲總是用〜表示。編寫宏的宏並沒有真正改變它。只需添加一個級別:~~'。

+0

實際上〜'是不必要的在你的例子中導致符號捕獲。 (defmacro FOO [名稱&體] '(DEFN〜名[巴](的println巴) 〜@體)) 足以引起符號捕獲和它可以根據代碼之前它或之後引起一個運行時錯誤或編譯時錯誤。 – 2010-05-30 21:46:11

+1

Jeremy Wall:您的示例將無法工作,因爲一旦宏展開,bar將變爲名稱空間限定,導致編譯器因爲無法綁定到名稱空間限定符號而陷入困境。 – Brian 2010-05-31 02:11:33