Un aspetto divertente delle macro è che ti danno la possibilità di espandere la sintassi del tuo lisp e aggiungere nuove funzionalità sintattiche, e questo accade solo perché gli argomenti passati a una macro verranno valutati solo in fase di esecuzione, e non a il tempo di compilare la tua macro. Ad esempio le macro del primo / ultimo thread in clojure -> ->>
non valutano i loro argomenti di espressione, altrimenti questi risultati valutati non potrebbero accettare altro (diciamo (+ 3) => 3
e 3
non è una funzione che potrebbe accettare il tuo primo argomento principale in qualcosa come (-> 4 (+ 3))
).
Ora, se mi piace questa sintassi e voglio aggiungerla alla mia implementazione Common Lisp (che non ce l'ha) posso aggiungerla definendo una macro io stesso. Qualcosa del genere:
;;; in SBCL
;;; first thread:
(defmacro -> (x &rest more)
(loop for m in more
for n = '(,(first m) ,x ,@(rest m)) then '(,(first m) ,n ,@(rest m))
finally (return n)))
;;; last thread:
(defmacro ->> (x &rest more)
(loop for m in more
for n = '(,(first m) ,@(rest m) ,x) then '(,(first m) ,@(rest m) ,n)
finally (return n)))
Ora sarei in grado di usarli in Common Lisp nello stesso modo in Clojure:
(-> #'+
(mapcar '(2 3 4) '(1 2 3))) ;; => (3 5 7)
(->> #'>
(sort '(9 8 3 5 7 2 4))) ;; => (9 8 7 5 4 3 2)
Forse vuoi anche avere una nuova sintassi per la funzione range
di clojure con le tue parole chiave per la tua sintassi, qualcosa del tipo:
(from 0 to 10) ;=> (0 1 2 3 4 5 6 7 8 9)
(from 0 to 10 by 0.5) ;;=> (0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5)
può essere definito come questa macro:
(defmacro from
"Just another range!"
[x y z & more]
'(when (= '~y '~'to)
(if '~more (range ~x ~z (second '~more))
(range ~x ~z))))
o aggiungi qualcosa di simile a Common Lisp (è solo per esempio poiché tutti questi possono già essere fatti nelle lingue ovviamente!):
(defmacro from (x y z &rest r)
'(when (eql ',y 'to)
(if (and ',r (eql (first ',r) 'by))
(loop for i from ,x to ,z by (second ',r) collect i)
(loop for i from ,x to ,z collect i))))
(from 0 to 10) ;=> (0 1 2 3 4 5 6 7 8 9 10)
(from 0 to 5 by 1/2) ;=> (0 1/2 1 3/2 2 5/2 3 7/2 4 9/2 5)