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 nella lista di proprietà del simbolo.
Ora possiamo scrivere attorno a una definizione di funzione:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Se vogliamo aggiungere una sintassi di fantasia come in Python, scriviamo una macro del lettore . Una macro reader ci permette di programmare sul livello della sintassi dell'espressione s:
(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 Lisp reader legge sopra a:
(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 di lettori.
Effettivamente farei la traduzione sopra nel 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 lo vede ancora come una cosiddetta forma di primo livello - il compilatore * di file tratta specialmente i moduli di livello superiore, ad esempio aggiunge informazioni su di essi nel tempo di compilazione ambiente . Nell'esempio sopra possiamo vedere che la macro guarda nel codice sorgente ed estrae il nome.
Lisp reader legge l'esempio precedente in:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Che quindi espande la macro in:
(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 ricevono il codice sorgente, 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 in modo totalmente diverso. Quindi il risultato deve essere un codice Lisp valido. Ma se il codice generato utilizza anche una macro, la sintassi del codice incorporato nella chiamata alla macro potrebbe nuovamente essere una sintassi diversa. Un semplice esempio: si potrebbe scrivere una macro matematica che accetta 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 come questo:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Ci sono molti altri casi d'uso di macro in Lisp.