Un decorador es básicamente solo una función .
Ejemplo en Common Lisp:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
En la parte superior, la función es un símbolo (que sería devuelto por DEFUN
) y colocamos los atributos en la lista de propiedades del símbolo .
Ahora podemos escribirlo alrededor de una definición de función:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Si queremos agregar una sintaxis elegante como en Python, escribimos una macro de lector . Una macro lectora nos permite programar en el nivel de sintaxis de s-expresión:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
Entonces podemos escribir:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
El lector Lisp lee arriba para:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Ahora tenemos una forma de decoradores en Common Lisp.
Combinando macros y macros de lector.
En realidad, haría la traducción anterior en código real usando una macro, no una función.
(defmacro defdecorator (decorator arg form)
`(progn
,form
(,decorator ,arg ',(second form))))
(set-macro-character
#\@
(lambda (stream char)
(declare (ignore char))
(let* ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(defdecorator ,decorator ,arg ,form))))
El uso es como el anterior con la misma macro de lector. La ventaja es que el compilador de Lisp todavía lo ve como un llamado formulario de nivel superior : el compilador de archivos * trata especialmente los formularios de nivel superior, por ejemplo, agrega información sobre ellos en el entorno de tiempo de compilación . En el ejemplo anterior, podemos ver que la macro busca en el código fuente y extrae el nombre.
El lector Lisp lee el ejemplo anterior en:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Que luego se amplía macro en:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Las macros son muy diferentes de las macros de lector .
Las macros obtienen el código fuente, pueden hacer lo que quieran y luego devolver el código fuente. La fuente de entrada no necesita ser un código Lisp válido. Puede ser cualquier cosa y podría escribirse totalmente diferente. El resultado tiene que ser un código Lisp válido entonces. Pero si el código generado también usa una macro, entonces la sintaxis del código incrustado en la llamada a la macro podría ser nuevamente una sintaxis diferente. Un ejemplo simple: se podría escribir una macro matemática que aceptaría algún tipo de sintaxis matemática:
(math y = 3 x ^ 2 - 4 x + 3)
La expresión y = 3 x ^ 2 - 4 x + 3
no es un código Lisp válido, pero la macro podría, por ejemplo, analizarlo y devolver un código Lisp válido como este:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Hay muchos otros casos de uso de macros en Lisp.