2012-03-13 18 views
10

Voglio scrivere una macro che usi le funzioni dalla libreria clj-time. In uno spazio dei nomi vorrei chiamare la macro in questo modo:Spazio dei nomi confusione e macro

(ns budget.account 
    (:require [budget.time])) 

(budget.time/next-date interval frequency) 

La macro di prossima data sarà definita in un altro file in questo modo:

(ns budget.time 
    (:require [clj-time.core :as date])) 

(defmacro next-date [interval freq] 
    `(~interval ~freq)) 

Se la macro sono stati chiamati con i seguenti argomenti (intervallo di tempo budget/ora successiva e intervallo e freq dove "settimane" e "2" in modo ripetitivo allora l'estensione della macro sarebbe simile a questa (clj-time.core/settimane 2)

Ogni volta che provo questo dal REPL non può risolvere lo spazio dei nomi.

C'è un modo per forzare la macro a risolvere l'intervallo degli argomenti nello spazio dei nomi clj-time? Qual è il modo migliore per farlo?

Grazie!

+0

Perché è necessario scrivere una macro. Non potrebbe essere fatto da una funzione regolare? Ricorda: non dovresti mai scrivere una macro quando una funzione lo farà. – viebel

risposta

10

Le macro restituiscono un elenco che viene quindi valutato nello spazio dei nomi da cui viene chiamato, non nello spazio dei nomi in cui è definito. Ciò è diverso dalle funzioni che valutano nello spazio dei nomi in cui sono definite. Questo è perché le macro restituiscono il codice da eseguire, invece di eseguirlo.

se vado ad un altro spazio dei nomi, per esempio hello.core ed espandere una chiamata a next-date ottengo:

hello.core> (macroexpand-1 '(next-date weeks 2)) 
(weeks 2) 

poi, dopo l'espansione, settimane è stato risolto da hello.core, in cui è ovviamente non definito. per risolvere questo problema abbiamo bisogno che il simbolo restituito porti con sé le informazioni sul nome-spazio.

per fortuna è possibile risolvere esplicitamente un simbolo in un namespace con ns-resolve. Ci vuole uno spazio dei nomi e un simbolo e cerca di trovare nel ritorno nullo spazio dei nomi, se non è trovato

(ns-resolve 'clj-time.core (symbol "weeks")) 
#'clj-time.core/weeks 

prossimo la macro sarà tenuto un simbolo e un numero così possiamo fare a meno con la chiamata esplicita a symbol

(ns-resolve 'clj-time.core 'weeks) 
#'clj-time.core/weeks 

così ora solo bisogno di una funzione che consente di risolvere la funzione e quindi crea un elenco della funzione risolto seguito dal numero,

(defmacro next-date [interval freq] 
    (list (ns-resolve 'clj-time.core interval) freq)) 

Nella sopra macro non fa altro che effettuare una chiamata di funzione che è chiamata immediatamente, in modo da non avrete nemmeno bisogno di una macro per questo:

(defn next-date [interval freq] 
    ((ns-resolve 'clj-time.core interval) freq)) 
(next-date 'weeks 2) 
#<Weeks P2W> 

la versione non-macro richiede di citare l'intervallo perché non è necessario valutarlo prima di poterlo consultare. Quello che la macro ti compra davvero non deve includere la citazione, al costo di richiedere a tutti i chiamanti di richiedere clj-

ovviamente potresti anche solo richiedere clj-time ovunque, ma questo non è proprio il punto .

+1

È meglio richiedere clj-time ovunque o usare ns-resolve? – user1265564

+1

personalmente vedo che richiede clj-time ovunque come opzione più pulita. –

Problemi correlati