Warum kommen in fast allen modernen Programmiersprachen (Go, Rust, Kotlin, Swift, Scala, Nim, sogar Python der letzten Version) Typen immer nach dem Variablennamen in der Variablendeklaration und nicht vorher?
Warum x: int = 42
nicht int x = 42
?
Ist das letztere nicht besser lesbar als das erstere?
Ist es nur ein Trend oder gibt es wirklich wichtige Gründe für diese Lösung?
programming-languages
language-design
syntax
Andre Polykanine
quelle
quelle
Antworten:
Alle von Ihnen erwähnten Sprachen unterstützen die Typinferenz. Dies bedeutet, dass der Typ ein optionaler Teil der Deklaration in diesen Sprachen ist, da er intelligent genug ist, ihn selbst auszufüllen, wenn Sie einen Initialisierungsausdruck mit einem leicht zu bestimmenden Typ bereitstellen.
Dies ist wichtig, da die optionalen Teile eines Ausdrucks weiter nach rechts verschoben werden, wodurch die Mehrdeutigkeit der Syntaxanalyse verringert und die Konsistenz zwischen den Ausdrücken, die diesen Teil verwenden, und denjenigen, die dies nicht tun, erhöht wird. Es ist nur einfacher, eine Deklaration zu analysieren, wenn Sie wissen, dass das
var
Schlüsselwort und der Variablenname obligatorisch sind, bevor Sie zu den optionalen Elementen gelangen. In der Theorie, die alle diese Dinge macht es einfacher zu analysieren , für Computer sollten für die Menschen zu allgemeiner Lesbarkeit verbessern, aber das ist viel mehr umstritten.Dieses Argument wird besonders stark , wenn Sie alle optionale Typänderungs zu berücksichtigen , die eine „nicht-moderne“ Sprache wie C ++ hat, wie
*
für Zeiger,&
Referenzen,const
,volatile
und so weiter. Sobald Sie Kommas für mehrere Deklarationen eingeben, treten einige wirklich merkwürdige Unklarheiten auf, z. B.int* a, b;
das Fehlenb
eines Zeigers.Sogar C ++ unterstützt jetzt "type on the right" -Deklarationen in Form von
auto x = int{ 4 };
und hat einige Vorteile .quelle
var
bilden den größten Teil der Vereinfachung der Syntaxanalyse.var
Schlüsselworts nicht dem Zweck der Namens-Erst-Notation? Ich sehe nicht den Vorteil des Schreibensvar userInput: String = getUserInput()
überString userInput = getUserInput()
. Der Vorteil wäre nur relevant, wenn eruserInput = getUserInput()
auch zulässig ist, aber das würde implizites Deklarieren einer Bereichsvariablen implizieren, was ich ziemlich abscheulich finde.var userInput = getUserInput()
oderauto userInput = getUserInput()
, der Compiler kenntgetUserInput()
Rückgaben,String
so dass Sie es nicht mit dem Schlüsselwort type deduction angeben müssen.userInput = getUserInput()
ist natürlich vor dem deklarieren zu gebrauchen.Ihre Prämisse ist an zwei Fronten fehlerhaft:
Pascal, ML, CLU, Modula-2 und Miranda waren alle sehr einflussreiche Sprachen, so dass es nicht verwunderlich ist, dass dieser Stil von Typdeklarationen beliebt blieb.
Lesbarkeit ist Vertrautheitssache. Ich persönlich finde Chinesisch unleserlich, aber anscheinend tun es die Chinesen nicht. Nachdem ich Pascal in der Schule gelernt habe, mich mit Eiffel, F H, Haskell und Scala beschäftigt habe und mir TypeScript, Fortress, Go, Rust, Kotlin, Idris, Frege, Agda, ML, Ocaml, ... angesehen habe, sehe ich ganz natürlich aus .
Wenn es sich um einen Trend handelt, ist er ziemlich hartnäckig: Wie ich bereits erwähnte, reicht er in der Mathematik hundert Jahre zurück.
Einer der Hauptvorteile des Typs nach dem Bezeichner besteht darin, dass der Typ einfach weggelassen werden kann, wenn Sie ihn ableiten möchten. Wenn Ihre Erklärungen so aussehen:
Dann ist es trivial, den Typ wegzulassen und ihn so ableiten zu lassen:
Wenn der Typ wie folgt vor dem Bezeichner steht:
Dann wird es für den Parser immer schwieriger, einen Ausdruck von einer Deklaration zu unterscheiden:
Die Lösung, die Sprachdesigner dann normalerweise finden, ist die Einführung eines Schlüsselworts "Ich möchte keinen Typ schreiben", das anstelle des Typs geschrieben werden muss:
Das macht aber eigentlich nicht viel Sinn: Sie müssen grundsätzlich explizit einen Typ schreiben, der besagt, dass Sie keinen Typ schreiben. Huh? Es wäre viel einfacher und sinnvoller, es einfach wegzulassen, aber das macht die Grammatik viel komplexer.
(Und lassen Sie uns nicht einmal über Funktionszeigertypen in C sprechen .)
Die Designer mehrerer der oben genannten Sprachen haben sich zu diesem Thema geäußert:
Go FAQ (siehe auch: Go's Deklarationssyntax ):
Kotlin FAQ :
Programmierung in Scala :
Hinweis: Die Entwickler von Ceylon haben auch dokumentiert, warum sie die Präfixtypsyntax verwenden :
Persönlich finde ich ihr "Argument" viel weniger überzeugend als die anderen.
quelle
var
,auto
,let
oder dergleichen, nicht von welcher Seite der Variablen der Typdeklaration eingeschaltet ist.var Int x = 5
oderInt var x = 5
könnte sogar beides verkürzt werdenvar x = 5
."Your premise is flawed on two fronts"
Alle sind neuer als C # oder D.func
in Go.Pascal macht das übrigens auch und ist keine neue Sprache. Es war jedoch eine akademische Sprache, die von Grund auf neu entwickelt wurde.
Ich würde sagen, es ist semantisch klarer, mit dem Variablennamen zu beginnen. Der Typ ist nur ein technisches Detail. Wenn Sie Ihre Klasse so lesen möchten, wie es das Modell der Realität ist, ist es sinnvoll, die Namen Ihrer Entitäten an erster Stelle und deren technische Implementierung an letzter Stelle zu setzen.
C # und Java stammen aus C, daher mussten sie den "Stand der Technik" respektieren, um den erfahrenen Programmierer nicht zu verwirren.
quelle
In einem so einfachen Beispiel gibt es keinen großen Unterschied, aber machen wir es etwas komplexer:
int* a, b;
Dies ist eine tatsächliche, gültige Deklaration in C, die jedoch nicht so funktioniert, wie es intuitiv aussehen sollte. Es sieht so aus, als würden wir zwei Variablen vom Typ deklarieren
int*
, aber tatsächlich deklarieren wir eineint*
und eineint
.Wenn in Ihrer Sprache der Typ in den Deklarationen nach dem oder den Variablennamen steht, kann dieses Problem nicht auftreten.
quelle
x, y *int;
zwei*int
oder eins*int
und einsint
? Makingint*
oder*int
ein Token anstelle von zwei würde es lösen oder ein zusätzliches syntaktisches Element wie das verwenden:
, das in beide Richtungen funktionieren könnte.:
oderas
zwischen dem Namen und dem Typ verwendet.x, y int
ohne Trennzeichen.x, y *int
bedeutet "deklariere zwei Variablen, x und y, mit dem Typ Zeiger auf int".int[] x, y
als zwei Arrays. Es ist nur die alberne C-Syntax, die diese Modifikatoren als unäre Operatoren für die Variable behandelt (und eine Mischung aus Präfix und Postfix, nicht weniger), die Probleme verursacht.