Eccone uno che uso per il debug (in Clojure):
user=> (defmacro print-var [varname] `(println ~(name varname) "=" ~varname))
#'user/print-var
=> (def x (reduce * [1 2 3 4 5]))
#'user/x
=> (print-var x)
x = 120
nil
Ho dovuto fare i conti con una tabella hash arrotolata a mano in C ++, in cui il get
metodo ha preso come riferimento un riferimento a una stringa non const, il che significa che non posso chiamarlo con un valore letterale. Per semplificare la gestione, ho scritto qualcosa di simile al seguente:
#define LET(name, value, body) \
do { \
string name(value); \
body; \
assert(name == value); \
} while (false)
Mentre è improbabile che qualcosa di simile a questo problema si presenti in lisp, trovo particolarmente bello che tu possa avere macro che non valutano i loro argomenti due volte, ad esempio introducendo un vero e proprio binding. (Ammesso, qui avrei potuto aggirarlo).
Ricorro anche al brutto trucco di avvolgere roba in modo do ... while (false)
tale che tu possa usarla nell'allora parte di un if e avere ancora il lavoro dell'altra parte come previsto. Non è necessario questo in lisp, che è una funzione delle macro che operano sugli alberi di sintassi piuttosto che sulle stringhe (o sequenze di token, credo, nel caso di C e C ++) che poi subiscono l'analisi.
Esistono alcune macro di threading integrate che possono essere utilizzate per riorganizzare il codice in modo che legga in modo più pulito ("threading" come "seminare il codice insieme", non parallelismo). Per esempio:
(->> (range 6) (filter even?) (map inc) (reduce *))
Prende la prima forma (range 6)
e ne fa l'ultimo argomento della forma successiva (filter even?)
, che a sua volta viene trasformata nell'ultima discussione della forma successiva e così via, in modo tale che quanto sopra venga riscritto in
(reduce * (map inc (filter even? (range 6))))
Penso che la prima sia molto più chiara: "prendi questi dati, fai questo, poi fai quello, poi fai l'altro e abbiamo finito", ma è soggettivo; qualcosa che è oggettivamente vero è che leggi le operazioni nella sequenza in cui sono eseguite (ignorando la pigrizia).
Esiste anche una variante che inserisce la forma precedente come primo (anziché ultimo) argomento. Un caso d'uso è l'aritmetica:
(-> 17 (- 2) (/ 3))
Legge "prendi 17, sottrai 2 e dividi per 3".
Parlando di aritmetica, puoi scrivere una macro che aggiorna l'analisi della notazione, in modo da poter dire ad esempio (infix (17 - 2) / 3)
e sputerebbe fuori (/ (- 17 2) 3)
che ha lo svantaggio di essere meno leggibile e il vantaggio di essere un'espressione lisp valida. Questa è la parte in sublanguage DSL / data.