Je vais être franc: je ne comprends pas ce que signifie "utiliser pour la syntaxe, pas pour la sémantique". C'est pourquoi une partie de cette réponse traitera de la réponse de lunaryon , et l'autre partie sera ma tentative de répondre à la question d'origine.
Lorsque le mot «sémantique» utilisé dans la programmation, il se réfère à la façon dont l'auteur de la langue a choisi d'interpréter leur langue. Cela se résume généralement à un ensemble de formules qui transforment les règles de grammaire du langage en quelques autres règles que le lecteur est censé connaître. Par exemple, Plotkin dans son livre Structural Approach to Operational Semantics utilise un langage de dérivation logique pour expliquer à quoi correspond la syntaxe abstraite (qu'est-ce que cela signifie d'exécuter un programme). En d'autres termes, si la syntaxe est ce qui constitue le langage de programmation à un certain niveau, alors il doit avoir une certaine sémantique, sinon c'est, eh bien ... pas un langage de programmation.
Mais, oublions pour le moment les définitions formelles, après tout, il est important de comprendre l'intention. Ainsi, il semble que lunaryon encourage ses lecteurs à utiliser des macros lorsqu'aucune nouvelle signification ne doit être ajoutée au programme, mais simplement une sorte d'abréviation. Pour moi, cela semble étrange et contraste fortement avec la façon dont les macros sont réellement utilisées. Voici des exemples qui créent clairement de nouvelles significations:
defun
et les amis, qui sont une partie essentielle du langage, ne peuvent pas être décrits dans les mêmes termes que vous décririez les appels de fonction.
setf
les règles décrivant le fonctionnement des fonctions sont inadéquates pour décrire les effets d'une expression comme (setf (aref x y) z)
.
with-output-to-string
modifie également la sémantique du code à l'intérieur de la macro. De même, with-current-buffer
et un tas d'autres with-
macros.
dotimes
et similaire ne peut pas être décrit en termes de fonctions.
ignore-errors
change la sémantique du code qu'il encapsule.
Il y a tout un tas de macros définies dans cl-lib
package, eieio
package et quelques autres bibliothèques presque omniprésentes utilisées dans Emacs Lisp qui, bien qu'elles puissent ressembler à des formes de fonction, ont des interprétations différentes.
Donc, si quoi que ce soit, les macros sont l'outil pour introduire une nouvelle sémantique dans un langage. Je ne peux pas penser à une autre façon de faire cela (du moins pas dans Emacs Lisp).
Quand je n'utiliserais pas de macros:
Quand ils n'introduisent pas de nouvelles constructions, avec une nouvelle sémantique (c'est-à-dire que la fonction ferait le travail tout aussi bien).
Lorsque c'est juste une sorte de commodité (c'est-à-dire créer une macro nommée mvb
qui se développe cl-multiple-value-bind
juste pour la raccourcir).
Lorsque je m'attends à ce que beaucoup de codes de gestion des erreurs soient cachés par la macro (comme cela a été noté, les macros sont difficiles à déboguer).
Quand je préférerais fortement les macros aux fonctions:
Lorsque les concepts spécifiques au domaine sont masqués par des appels de fonction. Par exemple, si l'on a besoin de passer le même context
argument à un tas de fonctions qui doivent être appelées successivement, je préférerais les envelopper dans une macro, où l' context
argument est caché.
Lorsque le code générique sous forme de fonction est trop verbeux (les constructions d'itération nue dans Emacs Lisp sont trop verbeuses à mon goût et exposent inutilement le programmeur aux détails d'implémentation de bas niveau).
Illustration
Ci-dessous est la version édulcorée destinée à donner une intuition sur ce que l'on entend par sémantique en informatique.
Supposons que vous ayez un langage de programmation extrêmement simple avec juste ces règles de syntaxe:
variable := 1 | 0
operation := +
expression := variable | (expression operation expression)
avec ce langage, nous pouvons construire des "programmes" comme (1+0)+1
ou 0
ou ((0+0))
etc.
Mais tant que nous n'avons pas fourni de règles sémantiques, ces "programmes" n'ont aucun sens.
Supposons que nous équipions maintenant notre langage des règles (sémantiques):
0+0 0+1 1+0 1+1 (exp)
---, ---, ---, ---, -----
0 1+0 1 0 exp
Maintenant, nous pouvons réellement calculer en utilisant ce langage. Autrement dit, nous pouvons prendre la représentation syntaxique, la comprendre de manière abstraite (en d'autres termes, la convertir en syntaxe abstraite), puis utiliser des règles sémantiques pour manipuler cette représentation.
Lorsque nous parlons de la famille de langages Lisp, les règles sémantiques de base de l'évaluation des fonctions sont à peu près celles du lambda-calcul:
(f x) (lambda x y) x
-----, ------------, ---
fx ^x.y x
Et les mécanismes macro citant-extension sont également connus comme outils de méta-programmation, à savoir qu'ils permettent de parler au sujet du programme, au lieu de la programmation juste. Ils y parviennent en créant une nouvelle sémantique à l'aide de la "méta-couche". L'exemple d'une macro aussi simple est:
(a . b)
-------
(cons a b)
Ce n'est pas réellement une macro dans Emacs Lisp, mais cela aurait pu l'être. Je n'ai choisi que pour des raisons de simplicité. Notez qu'aucune des règles sémantiques définies ci-dessus ne s'appliquerait à ce cas puisque le candidat le plus proche (f x)
interprète f
comme une fonction, alors qu'il a
n'est pas nécessairement une fonction.