Sarò schietto: non capisco cosa significhi "usare per la sintassi, non per la semantica". Questo è il motivo per cui parte di questa risposta affronterà la risposta di Lunaryon , e l'altra parte sarà il mio tentativo di rispondere alla domanda originale.
Quando la parola "semantica" utilizzata nella programmazione, si riferisce al modo in cui l'autore della lingua ha scelto di interpretare la propria lingua. Questo in genere si riduce a una serie di formule che trasformano le regole grammaticali della lingua in alcune altre regole che si presume che il lettore abbia familiarità. Ad esempio, Plotkin nel suo libro Approccio strutturale alla semantica operativa utilizza un linguaggio logico di derivazione per spiegare a cosa viene valutata la sintassi astratta (cosa significa eseguire un programma). In altre parole, se la sintassi è ciò che costituisce il linguaggio di programmazione su un certo livello, allora deve avere una semantica, altrimenti è, beh ... non un linguaggio di programmazione.
Ma per il momento dimentichiamo le definizioni formali, dopo tutto, è importante capire l'intenzione. Quindi, sembra che Lunaryon incoraggerebbe i suoi lettori a usare le macro quando non si aggiunge alcun nuovo significato al programma, ma solo una sorta di abbreviazione. Per me questo sembra strano e in netto contrasto con il modo in cui le macro vengono effettivamente utilizzate. Di seguito sono riportati esempi che creano chiaramente nuovi significati:
defun
e gli amici, che sono una parte essenziale della lingua, non possono essere descritti negli stessi termini in cui descriveresti le chiamate di funzione.
setf
le regole che descrivono come funzionano le funzioni sono inadeguate per descrivere gli effetti di un'espressione simile (setf (aref x y) z)
.
with-output-to-string
cambia anche la semantica del codice all'interno della macro. Allo stesso modo, with-current-buffer
e un sacco di altre with-
macro.
dotimes
e simili non possono essere descritti in termini di funzioni.
ignore-errors
cambia la semantica del codice che avvolge.
Ci sono un sacco di macro definite in cl-lib
pacchetto, eieio
pacchetto e alcune altre librerie quasi onnipresenti utilizzate in Emacs Lisp che, sebbene possano sembrare che le forme di funzioni abbiano interpretazioni diverse.
Quindi, semmai, le macro sono lo strumento per introdurre una nuova semantica in una lingua. Non riesco a pensare a un modo alternativo di farlo (almeno non in Emacs Lisp).
Quando non userei le macro:
Quando non introducono nuovi costrutti, con nuove semantiche (ovvero la funzione farebbe altrettanto bene il lavoro).
Quando questa è solo una sorta di convenienza (ovvero creare una macro denominata mvb
che si espande cl-multiple-value-bind
solo per renderla più breve).
Quando mi aspetto che un sacco di errori nella gestione del codice vengano nascosti dalla macro (come è stato notato, le macro sono difficili da eseguire il debug).
Quando preferirei fortemente le macro rispetto alle funzioni:
Quando i concetti specifici del dominio sono oscurati dalle chiamate di funzione. Ad esempio, se uno ha bisogno di passare lo stesso context
argomento a un gruppo di funzioni che devono essere chiamate in successione, preferirei avvolgerle in una macro, dove l' context
argomento è nascosto.
Quando il codice generico in forma di funzione è eccessivamente dettagliato (i costrutti di iterazione nuda in Emacs Lisp sono troppo dettagliati per i miei gusti ed espongono inutilmente il programmatore ai dettagli di implementazione di basso livello).
Illustrazione
Di seguito è la versione annacquata intesa a dare un'idea dell'intuizione di ciò che si intende per semantica in informatica.
Supponiamo di avere un linguaggio di programmazione estremamente semplice con solo queste regole di sintassi:
variable := 1 | 0
operation := +
expression := variable | (expression operation expression)
con questo linguaggio possiamo costruire "programmi" come (1+0)+1
o 0
o ((0+0))
ecc.
Ma finché non forniamo regole semantiche, questi "programmi" sono privi di significato.
Supponiamo che ora dotiamo la nostra lingua delle regole (semantiche):
0+0 0+1 1+0 1+1 (exp)
---, ---, ---, ---, -----
0 1+0 1 0 exp
Ora possiamo effettivamente calcolare usando questa lingua. Cioè, possiamo prendere la rappresentazione sintattica, capirla in modo astratto (in altre parole, convertirla in sintassi astratta), quindi usare regole semantiche per manipolare questa rappresentazione.
Quando parliamo della famiglia di lingue Lisp, le regole semantiche di base della valutazione delle funzioni sono all'incirca quelle del lambda-calcolo:
(f x) (lambda x y) x
-----, ------------, ---
fx ^x.y x
Citando e meccanismi macro-espansione sono anche noti come strumenti meta-programmazione, e cioè permettono uno di parlare circa il programma, invece di programmazione. Raggiungono questo obiettivo creando una nuova semantica usando il "meta layer". L'esempio di una macro così semplice è:
(a . b)
-------
(cons a b)
Questa non è in realtà una macro in Emacs Lisp, ma avrebbe potuto esserlo. Ho scelto solo per semplicità. Si noti che nessuna delle regole semantiche sopra definite si applicherebbe a questo caso poiché il candidato più vicino (f x)
interpreta f
una funzione, mentre a
non è necessariamente una funzione.