Macros

Remarque:
Ne pas utiliser de macro si on peut s'en passer.
En particulier cette remarque s'applique ici car un (defun square (x) ....)
ferait très bien l'affaire mais cet exemple est choisi pour sa simplicité.

Fonctionement d'une macro:
Phase 1: la macro developpe (expand) le code lisp attendu.
Phase 2: Le code lisp est exécuté.
La difficulté est d'obtenir le code attendu par la phase 1.

Exemple 1
On crée une macro square qui calcule le carré de son argument.
* (defmacro square (x) (* x x))
SQUARE

* (square 4)
16

* (defvar az 4)
AZ

* (square az)
debugger invoked on a SIMPLE-TYPE-ERROR in thread
#:
Argument X is not a NUMBER: AZ

Le problème est que la valeur de az n'est pas transmise à la macro.
Une solution est d'utiliser la backquote ` (1)

* (defmacro square (x) `(* ,x ,x))
SQUARE

*(square az)
16

Cette fois cela fonctionne avec les variables mais il y a enccore un problème:

* (setf az 4)
* (square (incf az))
30  (valeur attendue 25)

Utilisons la fonction macroexpand pour voir ce qu'il se passe.

* (macroexpand '(square (incf az)))
(* (INCF AZ) (INCF AZ))
T

La variable az est incrémentée deux fois, donc on calcule 5*6.

Solution:
(defmacro square (x)
`(let ((temp ,x))
(* temp temp)))

* (setf az 4)
* (square (incf az))
25

* (macroexpand '(square (incf az)))
(LET ((TEMP (INCF AZ)))
(* TEMP TEMP))
T

(1) A propos des backquotes.
La macro square aurait pu s'écrire sans la backquote:  (defmacro square (x) (list '* x x))
Mais la backquote rend les expressions plus simples à lire.
Si on remplace une quote par une backquote on obtient exactement le même résultat:
`(a z e r t y)  =>  (a z e r t y)
`(a z (+ 2 3) r t y)  =>  (a z (+ 2 3) r t y)

La virgule, à l'intérieur de la backquote, permet l'évaluation du symbole qui la suit.
(defvar e  "macro")
`(a z ,e r t y)   =>  (a z "macro" r t y)
`(a z ,(+ 2 3) r t y)  =>  (a z 5 r t y)