La macro-génération est une technique « vieille comme le monde », à la fois largement utilisée et largement critiquée. On l'utilise dans deux optiques :
eval.Le gros avantage de la macro-génération en est sa simplicité. Mais cette simplicité ne doit pas masquer certains dangers qui lui sont liés :
#define max1(a,b) ((a<b)? (b):(a))#define max1(a,b) ((a<b)? b:a)On trouve dans [KoW 87] une syntaxe pour les macro-définitions en Lisp qui cherche à en faciliter la compréhension : au lieu de proposer classiquement une fonction qui permette de tester sur quelques exemples la macro-génération, on propose un moyen d'expression qui fait ressembler la macro-définition à sa forme macro-générée. Dans [TrY 80] on cherche à réduire l'initiative laissée au programmeur à l'utilisation d'une macro-définition (COBOL) : c'est la macro-définition syntaxique, où les paramètres effectifs doivent répondre à certaines contraintes d'ordre syntaxique qu'on impose à la définition.
1. Les données
On regarde ici la première fonction "intéressante" : *putphyl :
(de *putphyl (nomphyl attphyl)
(let ((phyl (makephyl nomphyl attphyl)))
(ins phylt phyl)
(mapv '(lambda (oper)
(let ((drap (makedrap)))
(ins (phyloper oper) drap)
(ins (operphyl phyl) drap)))
opert))
(*putphyl-ana nomphyl attphyl))
On retrouve dans cette fonction les principales facultés d'abstraction du langage Lisp :
phylt et opert : elles permettent de réaliser les « effets de bord » ;makephyl, phyloper, ... ; on nomme la propriété sans savoir quelle est son implantation – il s'agit peut-être de l'appel d'un traitement, peut-être aussi d'un accès (macro-généré) à un élément de la structure de données ;ins : parce que le langage est a-typé, la définition d'une fonction générique est particulièrement simple (et particulièrement peu contrôlée aussi) ;mapv sur la lambda-expression.Les premiers points illustrent les possibilités d'abstraction de données du langage ; le dernier point concerne les traitements : c'est celui-là qu'on regarde maintenant.
mapvMapv n'est pas (dans LeLisp V15.2) une fonction prédéfinie. C'est l'application de mapc (prédéfinie) sur le type liste – les listes pour lesquelles le premier élément n'est pas significatif. Ceci suggère évidemment l'emploi d'une macro-définition
(mapv fct ref1 ... refN)
; donne :
(mapc fct (cdr ref1) ... (cdr refN))
De même si mapc n'était pas prédéfinie, on introduirait la macro-définition :
(mapc fct lst1 ... lstN)
; donne :
(let ((aux1 lst1) ... (auxN lstN))
(while (and aux1 ... auxN)
(fct (nextl aux1) ... (nextl auxN))))
On pourrait ensuite affiner nextl, si la fonction n'était pas prédéfinie :
(nextl arg)
; donne :
(prog1
(car arg)
(setq arg (cdr arg)))
On affinerait ensuite prog1, etc. ,jusqu'à arriver aux symboles primitifs implantés effectivement par le langage.
On voit donc que le langage permet une « expression de haut niveau » du problème (mapc) sur les éléments primitifs « de bas niveau » (while, next, ...) par des schémas de transformation qui restent transparents à l'utilisateur – c'est l'interprète Lisp qui se charge de réécrire le programme.
Pourquoi présenter cet aspect du langage sous la rubrique « macro-génération » ? c'est que volontairement on se place dans l'optique d'une exécution efficace des programmes : dans l'absolu, parce qu'il est toujours désagréable d'attendre inutilement ; pour les domaines spécifiques où l'efficacité est un impératif crucial – le Temps Réel. Si l'on se plaçait dans une autre optique, on pourrait se limiter à toujours programmer en Lisp, dont la puissance d'expression est suffisante pour couvrir la totalité des techniques de programmation offertes dans les langages – H. Abelson et G. Sussman donnent dans [AbS 83] un panorama complet de la programmation par le biais du lanage Scheme, un dialecte de Lisp.
Un langage de « haut niveau », comme Ada, est assez rebelle à l'expression de l'affinage d'un algorithme dans les termes du langage.
Pour représenter la fonction mapv, on pourrait chercher à mettre encore en pratique la généricité d'Ada, mais ceci semble mal adapté ; il faudrait :
mapv par le type d'objet (PHYL, OPER, DRAP) assorti de "faux sous-programmes" en paramètre et par le paramètre fonctionnel fct, lequel aurait a priori un nombre quelconque d'arguments ;mapv instancier tous les paramètres (les "vrais" et les "faux").Plus simplement on recopie le code et de ce fait on disperse la connaissance.
Dans l'exemple, la relative simplicité de la fonction Lisp permet de facilement identifier les étapes de dérivation du programme Lisp dans le texte du programme Ada :

schéma d'identification des programmes Lisp et Ada
En inversant le rôle des partenaires – Lisp et Ada – on perd en automatisation du processus de traduction mais on gagne en faisabilité. Le programme est écrit en Ada, les occurrences d'apparition de la fonction mapv sont les occurrences d'utilisation su « texte » mapv : par affinage, via les « textes », du programme Ada, on reconstruit formellement la fonction Lisp, mais en travaillant dans le langage « exigeant » – « exigeant », si l'on admet que Lisp est plutôt un langage « accomodant ».