Der Begriff "Orthogonalität" ist ein Laienbegriff für einen präzisen mathematischen Begriff: Die Sprachbegriffe bilden eine anfängliche Algebra (siehe Wikipedia).
Es bedeutet im Grunde "es gibt eine 1-1 Entsprechung zwischen Syntax und Bedeutung". Das bedeutet: Es gibt genau eine Möglichkeit, Dinge auszudrücken, und wenn Sie einen Ausdruck an einer bestimmten Stelle platzieren können, können Sie auch einen anderen Ausdruck dort platzieren.
Eine andere Möglichkeit, über "orthogonal" nachzudenken, besteht darin, dass die Syntax dem Substitutionsprinzip folgt. Wenn Sie beispielsweise eine Anweisung mit einem Steckplatz für einen Ausdruck haben, kann jeder Ausdruck dort abgelegt werden, und das Ergebnis ist immer noch ein syntaktisch gültiges Programm. Darüber hinaus, wenn Sie ersetzen
Ich möchte betonen, dass "Bedeutung" kein Rechenergebnis impliziert. Es ist klar, dass 1 + 2 und 2 + 1 beide gleich 3 sind. Die Begriffe sind jedoch unterschiedlich und implizieren eine andere Berechnung, selbst wenn sie das gleiche Ergebnis haben. Die Bedeutung ist unterschiedlich, genauso wie zwei Sortieralgorithmen unterschiedlich sind.
Möglicherweise haben Sie von "Abstract Syntax Tree" (AST) gehört. Das Wort "abstrakt" bedeutet hier genau "orthogonal". Technisch gesehen sind die meisten ASTs nicht abstrakt!
Vielleicht haben Sie von der Programmiersprache "C" gehört? Die Notation vom Typ C ist nicht abstrakt. Erwägen:
int f(int);
Hier ist also eine Funktionsdeklaration, die den Typ zurückgibt int
. Der Typ eines Zeigers auf diese Funktion ist gegeben durch:
int (*)(int)
Beachten Sie, dass Sie den Typ der Funktion nicht schreiben können! C Typ Notation saugt bigtime! Es ist nicht abstrakt. Es ist nicht orthogonal. Angenommen, wir möchten eine Funktion erstellen, die den obigen Typ anstelle von int akzeptiert:
int (*) ( int (*)(int) )
Alles in Ordnung .. aber .. was ist, wenn wir es stattdessen zurückgeben möchten:
int (*)(int) (*) (int)
Woops! Ungültig. Fügen wir Parens hinzu:
(int (*)(int)) (*) (int)
Woops! Das funktioniert auch nicht. Wir müssen das tun (es ist der einzige Weg!):
typedef int (intintfunc*) (int);
intintfunc (*)(int)
Jetzt ist es in Ordnung, aber hier ein typedef verwenden zu müssen, ist schlecht. C saugt. Es ist nicht abstrakt. Es ist nicht orthogonal. So machen Sie das in ML:
int -> (int -> int)
Wir verurteilen C auf Syntaxebene.
Ok, jetzt lass uns C ++ auspeitschen. Wir können die Dummheit oben mit Vorlagen beheben und eine ML-ähnliche Notation erhalten (mehr oder weniger):
fun<int, int>
fun< fun<int,int>, int>
Das eigentliche Typensystem ist jedoch durch Referenzen grundlegend fehlerhaft: Wenn T
es sich um einen Typ handelt, handelt es sich dann um T&
einen Typ? Die Antwort ist waffelig: Wenn Sie auf Syntaxebene einen Typ U = T & haben, ist U & zulässig, aber es bedeutet nur T &: Eine Referenz auf eine Referenz ist die ursprüngliche Referenz. Das ist scheiße! Es bricht die Eindeutigkeitsanforderung semantisch. Schlimmer noch: T & & ist syntaktisch nicht zulässig: Dies verstößt gegen das Substitutionsprinzip. Daher brechen C ++ - Referenzen die Orthogonalität je nach Bindungszeit (Parsing oder Typanalyse) auf zwei verschiedene Arten. Wenn Sie verstehen wollen, wie man das richtig macht, gibt es kein Problem mit Zeigern!
Fast keine echten Sprachen sind orthogonal. Sogar das Schema, das eine große Klarheit des Ausdrucks vorgibt, ist es nicht. Es kann jedoch beurteilt werden, dass viele gute Sprachen eine "ziemlich nahe an der orthogonalen Merkmalsbasis" haben, und dies ist eine gute Empfehlung für eine Sprache, die sowohl auf die Syntax als auch auf die zugrunde liegende Semantik angewendet wird.