Um aspecto divertido das macros é que elas oferecem a possibilidade de expandir a sintaxe do seu lisp e adicionar novos recursos sintáticos a ela, e isso acontece apenas porque os argumentos passados para uma macro são avaliados apenas no tempo de execução, e não no momento da compilando sua macro. Como exemplo, as primeiras / últimas macros de thread no clojure -> ->>
não avaliam seus argumentos de expressão, caso contrário, esses resultados avaliados não poderiam aceitar mais nada (digamos (+ 3) => 3
e 3
não é uma função que poderia aceitar seu primeiro argumento principal em algo como (-> 4 (+ 3))
).
Agora, se eu gosto dessa sintaxe e quero adicioná-la à minha implementação Common Lisp (que não a possui), posso adicioná-la definindo uma macro por conta própria. Algo assim:
;;; 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)))
Agora eu seria capaz de usá-los no Common Lisp da mesma maneira que no 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)
Talvez você também queira ter uma nova sintaxe para a range
função do clojure com suas próprias palavras-chave para sua sintaxe, algo como:
(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)
pode ser definido como esta macro:
(defmacro from
"Just another range!"
[x y z & more]
`(when (= '~y '~'to)
(if '~more (range ~x ~z (second '~more))
(range ~x ~z))))
ou adicione algo semelhante ao Common Lisp (é apenas por exemplo, pois tudo isso já pode ser feito nos idiomas, é claro!):
(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)