Warum folgt der Typ in modernen Programmiersprachen dem Variablennamen?

57

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 = 42nicht 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?

Andre Polykanine
quelle
3
Ja, das ist ein Trottel in Ordnung. Obwohl es sich speziell auf die Kotlin-Sprache bezieht, gilt die Frage (und ihre Antworten) für alle Sprachen, die diese Praxis haben.
Robert Harvey
2
Sorry Leute, ich habe die Frage zu Kotlin beim googeln gesehen, aber ich habe erneut darum gebeten, keine Antworten wie "Weil das Kotlin (Go, Rust, ...) Entwicklerteam sich so entschieden hat" zu bekommen. Ich war an einer allgemeineren Antwort interessiert: Warum kommt es zu einer Art Epidemie?
Andre Polykanine
2
Hihi, also Delphi, das (Object) Pascal ist und seit seiner Gründung, als es noch dunkel war, nach Variablennamen benannt wurde, ist wieder modern gewöhnt an. Als ich aus Delphi kam, ließ mich C # mit seinem Typ vor dem Variablennamen eine ganze Weile auf den Kopf klopfen.
Marjan Venema

Antworten:

64

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 varSchlü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, volatileund so weiter. Sobald Sie Kommas für mehrere Deklarationen eingeben, treten einige wirklich merkwürdige Unklarheiten auf, z. B. int* a, b;das Fehlen beines Zeigers.

Sogar C ++ unterstützt jetzt "type on the right" -Deklarationen in Form von auto x = int{ 4 };und hat einige Vorteile .

Ixrec
quelle
2
+1 für das Bestätigen der Existenz solcher obligatorischen Schlüsselwörter varbilden den größten Teil der Vereinfachung der Syntaxanalyse.
8bittree
2
Widerspricht die Existenz des varSchlüsselworts nicht dem Zweck der Namens-Erst-Notation? Ich sehe nicht den Vorteil des Schreibens var userInput: String = getUserInput()über String userInput = getUserInput(). Der Vorteil wäre nur relevant, wenn er userInput = getUserInput()auch zulässig ist, aber das würde implizites Deklarieren einer Bereichsvariablen implizieren, was ich ziemlich abscheulich finde.
kdb
2
@kdb in Sprachen wie C # und C ++ wäre es var userInput = getUserInput()oder auto userInput = getUserInput(), der Compiler kennt getUserInput()Rückgaben, Stringso dass Sie es nicht mit dem Schlüsselwort type deduction angeben müssen. userInput = getUserInput()ist natürlich vor dem deklarieren zu gebrauchen.
Wes Toleman
32

Warum kommen in fast allen modernen Programmiersprachen (Go, Rust, Kotlin, Swift, Scala, Nim, sogar Python letzte Version) Typen immer nach der Variablendeklaration und nicht vorher?

Ihre Prämisse ist an zwei Fronten fehlerhaft:

  • Es gibt neue Programmiersprachen, deren Typ vor dem Bezeichner steht. C♯, D oder Ceylon zum Beispiel.
  • Wenn der Typ nach dem Bezeichner kein neues Phänomen ist, geht er mindestens auf Pascal (Entwurf 1968–1969, Erscheinen 1970) zurück, wurde aber tatsächlich in der mathematischen Typentheorie verwendet, die ~ 1902 beginnt. Es wurde auch in ML (1973), CLU (1974), Hope (1970), Modula-2 (1977–1985), Ada (1980), Miranda (1985), Caml (1985), Eiffel (1985), Oberon verwendet (1986), Modula-3 (1986–1988) und Haskell (1989).

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.

Warum x: int = 42nicht int x = 42? Ist das letztere nicht besser lesbar als das erstere?

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 .

Ist es nur ein Trend oder gibt es wirklich sinnvolle Gründe für eine solche Lösung?

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:

val i: Int = 10

Dann ist es trivial, den Typ wegzulassen und ihn so ableiten zu lassen:

val i = 10

Wenn der Typ wie folgt vor dem Bezeichner steht:

Int i = 10

Dann wird es für den Parser immer schwieriger, einen Ausdruck von einer Deklaration zu unterscheiden:

i = 10

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:

var  i = 10; // C♯
auto i = 10; // C++

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 ):

    Warum sind Erklärungen rückwärts?

    Sie sind nur rückwärts, wenn Sie an C gewöhnt sind. In C wird eine Variable wie ein Ausdruck deklariert, der ihren Typ bezeichnet. Dies ist eine gute Idee, aber die Grammatik für Typ und Ausdruck lässt sich nicht gut miteinander kombinieren Die Ergebnisse können verwirrend sein. Betrachten Sie Funktionszeiger. Go trennt meistens Ausdrucks- und Typensyntax und vereinfacht die Dinge (die Verwendung von Präfixen *für Zeiger ist eine Ausnahme, die die Regel bestätigt). In C die Deklaration

    int* a, b;
    

    erklärt a, ein Zeiger zu sein, aber nicht b; in Go

    var a, b *int
    

    erklärt beide als Zeiger. Das ist klarer und regelmäßiger. Auch die :=argumentiert kurze Erklärung Form , dass eine vollständige Variablendeklaration sollte in die gleiche Reihenfolge präsentieren , wie :=so

    var a uint64 = 1
    

    hat den gleichen Effekt wie

    a := uint64(1)
    

    Das Parsen wird auch dadurch vereinfacht, dass für Typen eine eigene Grammatik vorhanden ist, die nicht nur die Ausdrucksgrammatik ist. Schlüsselwörter wie funcund chanhalten die Dinge klar.

  • Kotlin FAQ :

    Warum rechts Typdeklarationen?

    Wir glauben, dass dadurch der Code besser lesbar wird. Außerdem ermöglicht es einige nette syntaktische Funktionen. Beispielsweise ist es einfach, Typanmerkungen wegzulassen. Scala hat sich auch als ziemlich gut erwiesen, dies ist kein Problem.

  • Programmierung in Scala :

    Die Hauptabweichung von Java betrifft die Syntax für Typanmerkungen - in Java " variable: Type" statt " Type variable". Die Postfix-Syntax von Scala ähnelt Pascal, Modula-2 oder Eiffel. Der Hauptgrund für diese Abweichung liegt in der Typinferenz, bei der Sie häufig den Typ einer Variablen oder den Rückgabetyp einer Methode weglassen können. Mit der variable: TypeSyntax " " ist dies einfach - lassen Sie einfach den Doppelpunkt und den Typ weg. In der " Type variable" C- Syntax können Sie den Typ jedoch nicht einfach weglassen - es gibt keine Markierung mehr, mit der die Definition gestartet werden könnte. Sie benötigen ein alternatives Schlüsselwort, um als Platzhalter für einen fehlenden Typ zu fungieren (C♯ 3.0, das eine Art Rückschluss auf einen Typ zulässt, wird varfür diesen Zweck verwendet). Ein solches alternatives Keyword wirkt ad-hoc und weniger regelmäßig als Scalas Ansatz.

Hinweis: Die Entwickler von Ceylon haben auch dokumentiert, warum sie die Präfixtypsyntax verwenden :

Präfix anstelle von Postfix-Typanmerkungen

Warum folgen Sie C und Java, wenn Sie Typanmerkungen an die erste Stelle setzen, anstatt Pascal und ML, wenn Sie sie nach dem Deklarationsnamen setzen?

Weil wir das denken:

shared Float e = ....
shared Float log(Float b, Float x) { ... }

Ist einfach viel einfacher zu lesen als das:

shared value e: Float = .... 
shared function log(b: Float, x: Float): Float { ... }

Und wir verstehen einfach nicht, wie jemand anders denken könnte!

Persönlich finde ich ihr "Argument" viel weniger überzeugend als die anderen.

Jörg W. Mittag
quelle
4
Scheint die Fähigkeit , die Art zu verlassen und hat immer noch einfache Analyse durch die Zugabe von gewährt wird var, auto, letoder dergleichen, nicht von welcher Seite der Variablen der Typdeklaration eingeschaltet ist. var Int x = 5oder Int var x = 5könnte sogar beides verkürzt werden var x = 5.
8bittree
5
Ich finde es zwar gleich gut lesbar, wenn man sich erst einmal daran gewöhnt hat und das optionale Argument type stark ist, möchte aber darauf hinweisen, dass die IDE Variablennamen basierend auf dem Typ nicht automatisch vorschlagen kann. Ich vermisse das, wenn ich TypeScript schreibe.
Pierre Henry
"Your premise is flawed on two fronts" Alle sind neuer als C # oder D.
Det
3
"Aber das macht eigentlich nicht viel Sinn: Sie müssen grundsätzlich explizit einen Typ schreiben, der besagt, dass Sie keinen Typ schreiben." Nun, die Version mit der richtigen Schreibweise ist in Ihrem Beispiel genau die gleiche ... Ich bin auch anderer Meinung, dass sie keinen Sinn ergibt. Es macht genau so viel Sinn wie funcin Go.
Sopel,
4

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.

Martin Maat
quelle
3
Ada macht das auch so, aber Ada ist mit Sicherheit keine "akademische Sprache".
John R. Strohm
Ada tat es, weil es schwer von Pascal und Modula 2 abgeschnitten hat.
Gort the Robot
2
@StevenBurnap, eine schnelle Suche zeigt, dass Wirths erstes Buch über Modula-2 Mitte 1982 herauskam. Ada befand sich einige Jahre zuvor in der Entwicklung (DoD1 war 1977 oder so, wie ich mich erinnere) und wurde 1983 sowohl als ANSI-Standard als auch als MIL-STD standardisiert. Alle vier Teams, die Kandidaten für Ada eingereicht hatten, nahmen an PASCAL teil als Ausgangspunkt. (Bell Labs wurde vom DoD aufgefordert, eine Kandidatensprache auf der Grundlage von C einzureichen. Sie lehnten dies ab und sagten, C sei zu diesem Zeitpunkt noch nicht robust genug für die unternehmenskritische DoD-Software.)
John R. Strohm,
Ich muss mich falsch erinnern. Ich dachte, Wirth wäre in Ada verwickelt gewesen.
Gort the Robot
Pascal begann eine akademische Sprache aus, aber bis Ende der 90er Jahre war es am Rande des Seins , die Sprache zu schreiben Windows - Anwendungen über Delphi in, das heißt , bis Borland den Hai mit Preis sprang und ließ die Tür offen für Java und C # Kommen Sie und stehlen Sie den Preis. In der alten Windows-App-Welt sehen Sie immer noch eine Tonne Delphi.
Shayne
1

Warum x: int = 42nicht int x = 42? Ist das letztere nicht besser lesbar als das erstere?

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 eine int*und eine int.

Wenn in Ihrer Sprache der Typ in den Deklarationen nach dem oder den Variablennamen steht, kann dieses Problem nicht auftreten.

Mason Wheeler
quelle
4
Ich bin nicht sicher, warum das Verschieben der Typdeklaration danach dies beeinflussen würde. Ist x, y *int;zwei *intoder eins *intund eins int? Making int*oder *intein 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.
8bittree
@ 8bittree In jeder mir vertrauten Sprache, die Name-First-Deklarationen verwendet, wird ein zusätzliches Token wie :oder aszwischen dem Namen und dem Typ verwendet.
Mason Wheeler
2
Und in Go x, y *intbedeutet "deklariere zwei Variablen, x und y, mit dem Typ Zeiger auf int".
Vatine
10
C # verwendet die Präfixsyntax, behandelt sie jedoch int[] x, yals 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.
CodesInChaos