Un décorateur est fondamentalement juste une fonction .
Exemple en Common Lisp:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
Ci-dessus, la fonction est un symbole (qui serait retourné par DEFUN
) et nous mettons les attributs sur la liste des propriétés du symbole .
Maintenant, nous pouvons l'écrire autour d'une définition de fonction:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Si nous voulons ajouter une syntaxe sophistiquée comme en Python, nous écrivons une macro de lecture . Une macro de lecture nous permet de programmer au niveau de la syntaxe de l'expression s:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
On peut alors écrire:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
Le Lisp lecteur lit ci-dessus:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Nous avons maintenant une forme de décorateurs en Common Lisp.
Combinaison de macros et de macros de lecture.
En fait, je ferais la traduction ci-dessus en code réel en utilisant une macro, pas une fonction.
(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'utilisation est la même que ci-dessus avec la même macro de lecteur. L'avantage est que le compilateur Lisp le considère toujours comme un formulaire dit de niveau supérieur - le compilateur de fichiers * traite spécialement les formulaires de niveau supérieur, par exemple, il ajoute des informations à leur sujet au moment de la compilation environnement de . Dans l'exemple ci-dessus, nous pouvons voir que la macro examine le code source et extrait le nom.
Le lecteur Lisp lit l'exemple ci-dessus dans:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Qui obtient ensuite une macro développée en:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Les macros sont très différentes des macros de lecture .
Les macros font passer le code source, peuvent faire ce qu'elles veulent, puis renvoyer le code source. La source d'entrée n'a pas besoin d'être un code Lisp valide. Ça peut être n'importe quoi et ça pourrait être écrit totalement différent. Le résultat doit alors être un code Lisp valide. Mais si le code généré utilise également une macro, la syntaxe du code incorporé dans l'appel de macro pourrait à nouveau être une syntaxe différente. Un exemple simple: on pourrait écrire une macro mathématique qui accepterait une sorte de syntaxe mathématique:
(math y = 3 x ^ 2 - 4 x + 3)
L'expression y = 3 x ^ 2 - 4 x + 3
n'est pas du code Lisp valide, mais la macro pourrait par exemple l'analyser et retourner du code Lisp valide comme ceci:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Il existe de nombreux autres cas d'utilisation des macros en Lisp.