Un aspecto divertido de las macros es que le brindan la posibilidad de expandir la sintaxis de su lisp y agregarle nuevas características sintácticas, y esto sucede solo porque los argumentos pasados a una macro se evaluarán solo en tiempo de ejecución, y no en el momento de la ejecución. compilando tu macro. Como ejemplo, las macros de primer / último hilo en clojure -> ->>
no evalúan sus argumentos de expresión, de lo contrario, estos resultados evaluados no podrían aceptar nada más (por ejemplo, (+ 3) => 3
y 3
no es una función que podría aceptar su primer argumento principal en algo así (-> 4 (+ 3))
).
Ahora, si me gusta esta sintaxis y quiero agregarla a mi implementación de Common Lisp (que no la tiene), puedo agregarla definiendo una macro yo mismo. Algo como esto:
;;; 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)))
Ahora podría usarlos en Common Lisp de la misma manera que en 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)
Además, quizás desee tener una nueva sintaxis para la range
función de clojure con sus propias palabras clave para su sintaxis, algo así 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)
se puede definir 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))))
o agregue algo similar a Common Lisp (¡es solo por ejemplo, ya que todo esto ya se puede hacer en los idiomas, por supuesto!):
(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)