Ich werde stumpf sein: Ich verstehe nicht, was "Verwendung für die Syntax, nicht für die Semantik" bedeutet. Aus diesem Grund wird ein Teil dieser Antwort die Antwort von Lunaryon behandeln , und der andere Teil wird mein Versuch sein, die ursprüngliche Frage zu beantworten.
Wenn das Wort "Semantik" in der Programmierung verwendet wird, bezieht es sich auf die Art und Weise, wie der Autor der Sprache ihre Sprache interpretiert. Dies läuft in der Regel auf eine Reihe von Formeln hinaus, die Grammatikregeln der Sprache in andere Regeln umwandeln, mit denen der Leser vermutlich vertraut ist. Zum Beispiel verwendet Plotkin in seinem Buch Structural Approach to Operational Semantics eine logische Ableitungssprache, um zu erklären, zu welcher abstrakten Syntax die Auswertung führt (was bedeutet es, ein Programm auszuführen). Mit anderen Worten, wenn Syntax auf einer bestimmten Ebene die Programmiersprache ausmacht, muss sie eine gewisse Semantik haben, andernfalls ist sie keine Programmiersprache.
Aber lassen Sie uns für den Moment die formalen Definitionen vergessen, schließlich ist es wichtig, die Absicht zu verstehen. Es scheint also, als würde Lunaryon seine Leser dazu ermutigen, Makros zu verwenden, wenn dem Programm keine neue Bedeutung, sondern nur eine Abkürzung hinzugefügt werden soll. Für mich klingt das merkwürdig und steht in krassem Gegensatz zu der Art und Weise, wie Makros tatsächlich verwendet werden. Nachfolgend einige Beispiele, die eindeutig neue Bedeutungen schaffen:
defun
und Freunde, die ein wesentlicher Bestandteil der Sprache sind, können nicht mit den gleichen Begriffen beschrieben werden, mit denen Sie Funktionsaufrufe beschreiben würden.
setf
Die Regeln, die die Funktionsweise von Funktionen beschreiben, reichen nicht aus, um die Auswirkungen eines Ausdrucks wie diesen zu beschreiben (setf (aref x y) z)
.
with-output-to-string
ändert auch die Semantik des Codes innerhalb des Makros. Ähnliches with-current-buffer
und ein paar andere with-
Makros.
dotimes
und ähnliches kann nicht mit Funktionen beschrieben werden.
ignore-errors
Ändert die Semantik des umschlossenen Codes.
In cl-lib
package, eieio
package und einigen anderen fast allgegenwärtigen Bibliotheken, die in Emacs Lisp verwendet werden, sind eine ganze Reihe von Makros definiert , die wie Funktionsformen aussehen und unterschiedliche Interpretationen haben können.
Makros sind also eher das Werkzeug, um eine neue Semantik in eine Sprache einzuführen. Ich kann mir keinen alternativen Weg vorstellen, das zu tun (zumindest nicht in Emacs Lisp).
Wenn ich keine Makros verwenden würde:
Wenn sie keine neuen Konstrukte einführen, mit neuer Semantik (dh die Funktion würde den Job genauso gut machen).
Wenn dies nur eine Art Annehmlichkeit ist (z. B. das Erstellen eines Makros mit dem Namen, mvb
das cl-multiple-value-bind
nur zur Verkürzung erweitert wird).
Wenn ich erwarte, dass viele Fehlerbehandlungscodes vom Makro ausgeblendet werden (wie bereits erwähnt, ist das Debuggen von Makros schwierig).
Wenn ich Makros gegenüber Funktionen stark bevorzugen würde:
Wenn domänenspezifische Konzepte durch Funktionsaufrufe verdeckt werden. Wenn man zum Beispiel dasselbe context
Argument an eine Reihe von Funktionen übergeben muss, die nacheinander aufgerufen werden müssen, würde ich es vorziehen, sie in ein Makro zu packen, in dem das context
Argument verborgen ist.
Wenn generischer Code in Funktionsform übermäßig ausführlich ist (nackte Iterationskonstrukte in Emacs Lisp sind nach meinem Geschmack zu ausführlich und setzen den Programmierer unnötigerweise den Details der Implementierung auf niedriger Ebene aus).
Illustration
Nachfolgend finden Sie die verwässerte Version, die eine Vorstellung davon vermitteln soll, was unter Semantik in der Informatik zu verstehen ist.
Angenommen, Sie haben eine extrem einfache Programmiersprache mit nur diesen Syntaxregeln:
variable := 1 | 0
operation := +
expression := variable | (expression operation expression)
Mit dieser Sprache können wir "Programme" wie (1+0)+1
oder 0
oder ((0+0))
usw. konstruieren .
Aber solange wir keine semantischen Regeln angegeben haben, sind diese "Programme" bedeutungslos.
Angenommen, wir statten unsere Sprache jetzt mit den (semantischen) Regeln aus:
0+0 0+1 1+0 1+1 (exp)
---, ---, ---, ---, -----
0 1+0 1 0 exp
Jetzt können wir tatsächlich mit dieser Sprache rechnen. Das heißt, wir können die syntaktische Repräsentation nehmen, abstrakt verstehen (dh in abstrakte Syntax umwandeln) und dann semantische Regeln verwenden, um diese Repräsentation zu manipulieren.
Wenn wir über die Lisp-Sprachfamilie sprechen, sind die grundlegenden semantischen Regeln der Funktionsbewertung ungefähr die des Lambda-Kalküls:
(f x) (lambda x y) x
-----, ------------, ---
fx ^x.y x
Zitier- und Makro-Erweiterungsmechanismen werden auch als Meta-Programmier-Tools bezeichnet, dh sie ermöglichen es, über das Programm zu sprechen , anstatt nur zu programmieren. Sie erreichen dies, indem sie mithilfe der "Meta-Ebene" eine neue Semantik erstellen. Das Beispiel eines solchen einfachen Makros ist:
(a . b)
-------
(cons a b)
Dies ist eigentlich kein Makro in Emacs Lisp, aber es hätte sein können. Ich habe mich nur der Einfachheit halber entschieden. Beachten Sie, dass keine der oben definierten semantischen Regeln für diesen Fall gilt, da der nächste Kandidat als Funktion (f x)
interpretiert f
, obwohl dies a
nicht unbedingt eine Funktion ist.