Un decoratore è fondamentalmente solo una funzione .
Esempio in Common Lisp:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
In sopra la funzione è un simbolo (che verrebbe restituito da DEFUN
) e inseriamo gli attributi nell'elenco delle proprietà del simbolo .
Ora possiamo scrivere attorno ad una definizione di funzione:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Se vogliamo aggiungere una sintassi elaborata come in Python, scriviamo una macro di lettura . Una macro lettore ci consente di programmare a livello di sintassi di s-expression:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
Quindi possiamo scrivere:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
Il lettore Lisp legge sopra per:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Ora abbiamo una forma di decoratori in Common Lisp.
Combinazione di macro e macro del lettore.
In realtà farei sopra la traduzione in codice reale usando una macro, non una funzione.
(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))))
L'uso è come sopra con la stessa macro del lettore. Il vantaggio è che il compilatore Lisp vede ancora come un cosiddetto modulo di livello superiore - le forme * compilatore di file tratta di alto livello appositamente, ad esempio aggiunge informazioni su di loro in fase di compilazione ambiente . Nell'esempio sopra possiamo vedere che la macro esamina il codice sorgente ed estrae il nome.
Il lettore Lisp legge l'esempio sopra in:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Che poi si espande in macro:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Le macro sono molto diverse dalle macro dei lettori .
Le macro ottengono il codice sorgente passato, possono fare quello che vogliono e quindi restituire il codice sorgente. La sorgente di input non deve essere un codice Lisp valido. Può essere qualsiasi cosa e potrebbe essere scritto totalmente diverso. Il risultato deve quindi essere un codice Lisp valido. Ma se anche il codice generato utilizza una macro, la sintassi del codice incorporato nella chiamata macro potrebbe essere di nuovo una sintassi diversa. Un semplice esempio: si potrebbe scrivere una macro matematica che accetterebbe una sorta di sintassi matematica:
(math y = 3 x ^ 2 - 4 x + 3)
L'espressione y = 3 x ^ 2 - 4 x + 3
non è un codice Lisp valido, ma la macro potrebbe ad esempio analizzarlo e restituire un codice Lisp valido in questo modo:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Ci sono molti altri casi d'uso di macro in Lisp.