2015-04-14 11 views
8

Se scrivo una macro che utilizza il collegamento per creare un gensym che viene quindi associato come una variabile globale, lo stesso simbolo viene generato ripetutamente. Tuttavia, funziona correttamente se chiamo gensym manualmente. Molto semplici esempi:(gensym) è sempre univoco, `(symb #) non lo è - perché?

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

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

Il problema è evidente se si utilizza 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__)) 

Questo problema va via se chiamo gensym la strada più lunga:

(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 

Quindi, perché la differenza? Cosa mi manca?

+0

'foo #' è una specie di macro che viene sostituita prima che la definizione della macro venga elaborata? – galdre

risposta

11

Sintassi di sintassi con ` è in realtà un reader macro; la forma che segue è trasformata dal lettore (che traduce il testo in forme Clojure) prima della valutazione. Ciò significa che qualsiasi simbolo che termina con # all'interno della citazione della sintassi viene convertito in un simbolo generato automaticamente solo una volta, quando il testo viene letto per la prima volta; il simbolo generato automaticamente viene quindi inserito direttamente nella definizione della macro e appare testualmente nella macroexpanion ogni volta che viene richiamata la macro. Ciò può essere illustrato facilmente al REPL:

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

Il tipico caso di utilizzo simboli auto-gen'ed con # è quello di definire variabili locali all'interno di un let o fn forma indicata. Lì, non importa che lo stesso simbolo venga riutilizzato per più invocazioni di macro; deve solo essere unico all'interno di ogni invocazione. Ad esempio:

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

Ah-hah. Ero vicino, ma ero concentrato su 'foo #' piuttosto che su '' '' '. Grazie mille! – galdre

+1

Ottima spiegazione. Un altro passo sulla strada per capire le macro in clojure :) –

Problemi correlati