2015-04-14 7 views
8

Wenn ich ein Makro schreibe, das die symb# Verknüpfung verwendet, um ein Gensym zu erstellen, das dann als globale Variable gebunden wird, wird das exakt gleiche Symbol immer wieder generiert. Es funktioniert jedoch richtig, wenn ich gensym manuell aufrufen. Sehr einfache Beispiele:(Gensym) ist immer eindeutig, `(symb #) ist nicht - warum?

(defmacro indirection 
    [name & body] 
    `(do (def name# [email protected]) 
     (defn ~name [] name#))) 

(indirection foo 3) 
(foo) ; ⇒ 3 
(indirection goo 5) 
(goo) ; ⇒ 5 
(foo) ; ⇒ 5 

Das Problem ist offensichtlich, wenn Sie verwenden macroexpand:

(macroexpand '(indirection foo 3)) 
(do (def name__2863__auto__ 3) (clojure.core/defn foo [] name__2863__auto__)) 
(macroexpand '(indirection foo 3)) 
(do (def name__2863__auto__ 3) (clojure.core/defn foo [] name__2863__auto__)) 

Dieses Problem geht weg, wenn ich gensym der lange Weg rufen:

(defmacro redirection 
    [name & body] 
    (let [rename (gensym)] 
     `(do (def ~rename [email protected]) 
      (defn ~name [] ~rename)))) 

(redirection koo 3) 
(koo) ; ⇒ 3 
(redirection moo 5) 
(moo) ; ⇒ 5 
(koo) ; ⇒ 3 

Also, warum die Unterschied? Was vermisse ich?

+0

Ist 'foo #' selbst eine Art Makro, das ersetzt wird, bevor die Makrodefinition überhaupt verarbeitet wird? – galdre

Antwort

11

Syntax Quoting mit ` ist eigentlich ein reader macro; Die Form, die ihr folgt, wird vor der Auswertung vom Leser (der den Text in Clojure-Formen übersetzt) ​​transformiert. Dies bedeutet, dass jedes Symbol, das innerhalb der Syntax-Quotierung in endet, nur einmal in ein automatisch generiertes Symbol übersetzt wird, wenn der Text zum ersten Mal gelesen wird. Dieses automatisch generierte Symbol wird dann direkt in die Makrodefinition eingefügt und erscheint jedes Mal, wenn das Makro aufgerufen wird, wortwörtlich in der Makroexpansion. Dies kann leicht an der REPL dargestellt werden:

user=> `(foo bar#) 
(user/foo bar__2288__auto__) 
user=> `(foo bar#) 
(user/foo bar__2291__auto__) 

Der typische Anwendungsfall für Auto-gen'ed Symbole mit # ist lokale Variablen innerhalb einer zitierten let oder fn Form zu definieren. Dort spielt es keine Rolle, dass das gleiche Symbol für mehrere Makroaufrufe wiederverwendet wird; es muss nur innerhalb jedes Aufrufs eindeutig sein. Zum Beispiel:

(defmacro indirection 
    [name body] 
    `(let [name# ~body] 
    (defn ~name [] name#))) 
+2

Ah-hah. Ich war nah dran, aber ich konzentrierte mich auf das 'foo #' und nicht auf das '' '' '. Vielen Dank! – galdre

+1

Hervorragende Erklärung. Ein weiterer Schritt auf dem Weg zum Verständnis von Makros in clojure :) –

Verwandte Themen