Perché il tipo segue il nome della variabile nei moderni linguaggi di programmazione?


57

Perché in quasi tutti i linguaggi di programmazione moderni (Go, Rust, Kotlin, Swift, Scala, Nim, persino Python ultima versione) i tipi seguono sempre il nome della variabile nella dichiarazione della variabile, e non prima?

Perché x: int = 42e no int x = 42?
Il secondo non è più leggibile del primo?
È solo una tendenza o ci sono ragioni davvero significative dietro questa soluzione?



3
Sì, va bene uno scherzo. Sebbene si riferisca specificamente alla lingua di Kotlin, la domanda (e le sue risposte) si applica a tutte le lingue che hanno questa pratica.
Robert Harvey,


2
Scusate ragazzi, ho visto la domanda su Kotlin mentre cercavo su google, ma ho chiesto nuovamente di non ottenere risposte come "Perché il team di sviluppo di Kotlin (Go, Rust, ...) ha deciso in questo modo". Ero interessato a una risposta più comune: perché diventa una specie di epidemia?
Andre Polykanine,

2
Hihi, quindi Delphi, che è (Object) Pascal, e ha avuto tipi dopo nomi variabili sin dal suo inizio, molto indietro nel tempo nei secoli bui del secolo precedente, è di nuovo moderno;) A proposito, la leggibilità è molto influenzata da ciò che sei abituato a. Venendo da Delfi, C # con il suo tipo prima del nome var, mi ha fatto battere la testa per un bel po 'di tempo.
Marjan Venema,

Risposte:


64

Tutte le lingue che hai citato supportano l' inferenza del tipo , il che significa che il tipo è una parte facoltativa della dichiarazione in quelle lingue perché sono abbastanza intelligenti da riempirlo da soli quando fornisci un'espressione di inizializzazione che ha un tipo facilmente determinabile.

Ciò è importante perché mettere le parti facoltative di un'espressione più a destra riduce l'ambiguità di analisi e aumenta la coerenza tra le espressioni che usano quella parte e quelle che non lo fanno. È più semplice analizzare una dichiarazione quando sai che la varparola chiave e il nome della variabile sono entrambi obbligatori prima di arrivare al materiale facoltativo. In teoria, tutte quelle cose che rendono più semplice l'analisi dei computer dovrebbero migliorare la leggibilità complessiva anche per gli umani, ma è molto più discutibile.

Questo argomento diventa particolarmente forte se si considerano tutti i modificatori di tipo opzionali che una lingua "non-moderno" come il C ++ ha, come ad esempio *per i puntatori, &per i riferimenti, const, volatilee così via. Dopo aver inserito le virgole per più dichiarazioni, inizi a ottenere alcune strane ambiguità come int* a, b;non fare bun puntatore.

Anche C ++ ora supporta le dichiarazioni "tipo a destra" sotto forma di auto x = int{ 4 };, e presenta alcuni vantaggi .


2
+1 per riconoscere l'esistenza di parole chiave obbligatorie come varquella fornisce la maggior parte della semplificazione dell'analisi.
8bittree,

2
L'esistenza della varparola chiave non vanifica lo scopo della prima notazione? Non vedo il vantaggio di scrivere var userInput: String = getUserInput()sopra String userInput = getUserInput(). Il vantaggio sarebbe rilevante solo se userInput = getUserInput()fosse consentito, ma ciò implicherebbe la dichiarazione implicita di una variabile con ambito, che trovo piuttosto odiosa.
kdb,

2
@kdb in linguaggi come C # e C ++ sarebbe var userInput = getUserInput()o auto userInput = getUserInput(), il compilatore conosce getUserInput()ritorni Stringquindi non è necessario specificarlo con la parola chiave di deduzione del tipo. userInput = getUserInput()è ovviamente da utilizzare prima di dichiarare.
Wes Toleman,

32

Perché in quasi tutti i linguaggi di programmazione moderni (Go, Rust, Kotlin, Swift, Scala, Nim, persino Python ultima versione) i tipi vengono sempre dopo la dichiarazione delle variabili, e non prima?

La tua premessa è imperfetta su due fronti:

  • Esistono linguaggi di programmazione nuovi che hanno il tipo prima dell'identificatore. C♯, D o Ceylon, per esempio.
  • Avere il tipo dopo l'identificatore non è un nuovo fenomeno, risale almeno a Pascal (progettato nel 1968-1969, rilasciato nel 1970), ma in realtà è stato usato nella teoria del tipo matematica, che inizia ~ 1902. Fu anche usato in ML (1973), CLU (1974), Hope (1970), Modula-2 (1977-1985), Ada (1980), Miranda (1985), Caml (1985), Eiffel (1985), Oberon (1986), Modula-3 (1986-1988) e Haskell (1989).

Pascal, ML, CLU, Modula-2 e Miranda erano tutti linguaggi molto influenti, quindi non sorprende che questo stile di dichiarazioni di tipo rimase popolare.

Perché x: int = 42e no int x = 42? Il secondo non è più leggibile del primo?

La leggibilità è una questione di familiarità. Personalmente, trovo il cinese illeggibile, ma a quanto pare, i cinesi non lo fanno. Avendo imparato Pascal a scuola, dilettandosi con Eiffel, F♯, Haskell e Scala, e avendo guardato TypeScript, Fortress, Go, Rust, Kotlin, Idris, Frege, Agda, ML, Ocaml, ..., mi sembra del tutto naturale .

È solo una tendenza o ci sono ragioni davvero significative dietro una simile soluzione?

Se è una tendenza, è abbastanza persistente: come ho già detto, risale a cento anni fa in matematica.

Uno dei principali vantaggi di avere il tipo dopo l'identificatore è che è facile lasciarlo fuori se si desidera dedurlo. Se le tue dichiarazioni sono così:

val i: Int = 10

Quindi è banale tralasciare il tipo e farlo dedurre in questo modo:

val i = 10

Considerando che se il tipo precede l'identificatore in questo modo:

Int i = 10

Quindi inizia a diventare difficile per il parser distinguere un'espressione da una dichiarazione:

i = 10

La soluzione che i designer linguistici di solito escogitano è quella di introdurre una parola chiave "Non voglio scrivere un tipo" che deve essere scritta al posto del tipo:

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

Ma questo non ha molto senso: in pratica devi scrivere esplicitamente un tipo che dice che non scrivi un tipo. Eh? Sarebbe molto più semplice e sensato lasciarlo fuori, ma ciò rende la grammatica molto più complessa.

(E non parliamo nemmeno dei tipi di puntatore a funzione in C.)

I progettisti di alcune delle lingue summenzionate hanno approfondito questo argomento:

  • Vai alle FAQ (vedi anche: Sintassi della dichiarazione di Go ):

    Perché le dichiarazioni sono al contrario?

    Sono solo all'indietro se sei abituato a C. In C, l'idea è che una variabile sia dichiarata come un'espressione che denota il suo tipo, che è una buona idea, ma le grammatiche di tipo ed espressione non si mescolano molto bene e i risultati possono essere confusi; considerare i puntatori a funzione. Go separa principalmente la sintassi di espressioni e tipi e questo semplifica le cose (l'uso del prefisso *per i puntatori è un'eccezione che dimostra la regola). In C, la dichiarazione

    int* a, b;
    

    dichiara adi essere un puntatore ma non b; in Go

    var a, b *int
    

    dichiara entrambi di essere puntatori. Questo è più chiaro e più regolare. Inoltre, il :=modulo di dichiarazione breve sostiene che una dichiarazione variabile completa dovrebbe presentare lo stesso ordine :=così

    var a uint64 = 1
    

    ha lo stesso effetto di

    a := uint64(1)
    

    L'analisi è anche semplificata avendo una grammatica distinta per i tipi che non è solo la grammatica delle espressioni; parole chiave come funce chanmantenere le cose chiare.

  • FAQ di Kotlin :

    Perché le dichiarazioni dei tipi sono sulla destra?

    Riteniamo che renda il codice più leggibile. Inoltre, abilita alcune belle funzionalità sintattiche. Ad esempio, è facile lasciare fuori le annotazioni dei tipi. Scala ha anche dimostrato abbastanza bene che questo non è un problema.

  • Programmazione in Scala :

    La principale deviazione da Java riguarda la sintassi per le annotazioni dei tipi: è " variable: Type" anziché " Type variable" in Java. La sintassi del tipo postfisso di Scala ricorda Pascal, Modula-2 o Eiffel. Il motivo principale di questa deviazione ha a che fare con l'inferenza del tipo, che spesso consente di omettere il tipo di una variabile o il tipo restituito di un metodo. Usare la variable: Typesintassi " " è facile: basta lasciare i due punti e il tipo. Ma nella Type variablesintassi " " in stile C non puoi semplicemente tralasciare il tipo: non ci sarebbe più un marcatore per iniziare la definizione. Avresti bisogno di una parola chiave alternativa per essere un segnaposto per un tipo mancante (C♯ 3.0, che fa una deduzione di tipo, utilizza vara questo scopo). Una tale parola chiave alternativa sembra più ad-hoc e meno regolare dell'approccio di Scala.

Nota: i progettisti di Ceylon hanno anche documentato perché usano la sintassi del tipo di prefisso :

Prefisso invece delle annotazioni del tipo postfisso

Perché segui C e Java inserendo prima le annotazioni dei tipi, invece di Pascal e ML inserendole dopo il nome della dichiarazione?

Perché pensiamo questo:

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

È semplicemente molto più facile da leggere di questo:

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

E semplicemente non capiamo come si possa pensare diversamente!

Personalmente, trovo il loro "argomento" molto meno convincente degli altri.


4
Sembra che la possibilità di lasciare il tipo e hanno ancora semplice analisi viene concesso mediante l'aggiunta di var, auto, let, o simili, non da quale parte variabile della dichiarazione del tipo è acceso. var Int x = 5o addirittura Int var x = 5potrebbe essere abbreviato entrambi var x = 5.
8bittree

5
Anche se mi piace leggerlo ugualmente una volta che ti ci abitui e l'argomento del tipo facoltativo è forte, vorrei sottolineare che l'IDE non è in grado di proporre automaticamente nomi di variabili in base al tipo. Mi manca quando scrivo TypeScript.
Pierre Henry,

"Your premise is flawed on two fronts" Tutti quelli sono più recenti di C # o D.
Det

3
"Ma questo non ha molto senso: in pratica devi scrivere esplicitamente un tipo che dice che non scrivi un tipo." Bene, la versione digitata a destra è esattamente la stessa nel tuo esempio ... Inoltre non sono d'accordo sul fatto che non abbia senso. Ha esattamente tanto senso quanto funcin Go.
Sopel,

4

Anche Pascal lo fa, e non è una nuova lingua. Era un linguaggio accademico, progettato da zero.

Direi che è semanticamente più chiaro iniziare con il nome della variabile. Il tipo è solo un dettaglio tecnico. Se vuoi leggere la tua classe come il modello di realtà che è, ha senso mettere prima i nomi delle tue entità e la loro implementazione tecnica per ultima.

C # e java derivano da C, quindi dovevano rispettare la "tecnica nota", in modo da non confondere il programmatore esperto.


3
Anche Ada lo fa in questo modo, ma Ada non è certamente un "linguaggio accademico".
John R. Strohm,

Ada lo fece perché paralizzava pesantemente da Pascal e Modula 2.
Gort the Robot

2
@StevenBurnap, una rapida ricerca rivela che il primo libro di Wirth su Modula-2 è uscito a metà 1982. Ada era in fase di sviluppo diversi anni prima (DoD1 era nel 1977 o giù di lì, come ricordo), e standardizzato, sia come standard ANSI che come MIL-STD, nel 1983. Tutti e quattro i team che presentarono i candidati per Ada presero PASCAL come punti di partenza. (Bell Labs è stato invitato dal DoD a presentare un linguaggio candidato basato su C. Hanno sminuito, dicendo che C non era allora e non sarebbe mai stato abbastanza robusto per il software mission-critical DoD.)
John R. Strohm

Devo ricordare male. Pensavo che Wirth fosse stato coinvolto in Ada.
Gort il robot

Pascal ha iniziato una lingua accademica, ma alla fine degli anni '90 era sul punto di essere la lingua per scrivere app di Windows in via Delphi, fino a quando Borland non ha saltato lo squalo con i prezzi e lasciato la porta aperta per Java e C # a vieni a rubare il premio. Vedrai comunque una tonnellata di Delfi là fuori nel mondo delle app legacy di Windows.
Shayne,

1

Perché x: int = 42e no int x = 42? Il secondo non è più leggibile del primo?

In un esempio così semplice, non c'è molta differenza, ma rendiamolo un po 'più complesso: int* a, b;

Questa è una dichiarazione effettiva e valida in C, ma non fa ciò che sembra intuitivamente come dovrebbe fare. Sembra che stiamo dichiarando due variabili di tipo int*, ma in realtà stiamo dichiarando una int*e una int.

Se la tua lingua inserisce il tipo dopo il nome / i variabile nelle sue dichiarazioni, è impossibile riscontrare quel problema.


4
Non sono sicuro del motivo per cui spostare la dichiarazione di tipo dopo influirebbe su questo. Sono x, y *int;due *into uno *inte uno int? Fare int*o *intun token invece di due lo risolverebbe, o usando un ulteriore elemento sintattico come il :, che potrebbe funzionare in entrambe le direzioni.
8

@ 8bittree Ogni lingua che conosco che utilizza le dichiarazioni del primo nome usa un token aggiuntivo, come :o as, tra il nome e il tipo.
Mason Wheeler,


2
E in Go, x, y *intsignifica "dichiarare due variabili, xey, con il tipo pointer-to-int".
Vatine,

10
C # usa la sintassi del prefisso, ma tratta int[] x, ycome due array. È solo la stupida sintassi C che tratta quei modificatori come operatori unari sulla variabile (e un mix di prefisso e postfisso, non meno) che causa problemi.
CodesInChaos,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.