Mengapa tipe ini digunakan setelah nama variabel dalam bahasa pemrograman modern?

57

Mengapa hampir di semua bahasa pemrograman modern (Go, Rust, Kotlin, Swift, Scala, Nim, bahkan versi terakhir Python) selalu muncul setelah nama variabel dalam deklarasi variabel, dan bukan sebelumnya?

Kenapa x: int = 42dan tidak int x = 42?
Apakah yang terakhir tidak lebih mudah dibaca daripada yang pertama?
Apakah ini hanya tren atau adakah alasan yang benar-benar bermakna di balik solusi ini?

Andre Polykanine
sumber
3
Yap, itu penipuan baik-baik saja. Meskipun merujuk secara khusus pada bahasa Kotlin, pertanyaan (dan jawabannya) berlaku untuk semua bahasa yang memiliki praktik ini.
Robert Harvey
2
Maaf teman-teman, saya memang melihat pertanyaan tentang Kotlin ketika googling, tetapi saya meminta kembali untuk tidak mendapatkan jawaban seperti "Karena Kotlin (Go, Rust, ...) tim pengembang memutuskan seperti ini". Saya tertarik pada jawaban yang lebih umum: mengapa itu menjadi semacam epidemi?
Andre Polykanine
2
Hihi, begitu Delphi, yang (Object) Pascal, dan telah memiliki tipe setelah nama variabel sejak awal, jauh ketika ketika di zaman kegelapan abad sebelumnya, adalah modern lagi;) Btw, keterbacaan jauh dipengaruhi oleh apa yang Anda biasanya. Berasal dari Delphi, C # dengan tipenya sebelum nama var, membuat saya memukul kepala saya untuk beberapa waktu.
Marjan Venema

Jawaban:

64

Semua bahasa yang Anda sebutkan inferensi tipe dukungan , yang berarti tipe tersebut adalah bagian opsional dari deklarasi dalam bahasa-bahasa tersebut karena mereka cukup pintar untuk mengisinya sendiri ketika Anda memberikan ekspresi inisialisasi yang memiliki tipe yang mudah ditentukan.

Itu penting karena menempatkan bagian opsional ekspresi lebih jauh ke kanan mengurangi ambiguitas penguraian, dan meningkatkan konsistensi antara ekspresi yang menggunakan bagian itu dan yang tidak. Hanya lebih mudah untuk mengurai deklarasi ketika Anda tahu varkata kunci dan nama variabel keduanya wajib sebelum Anda sampai ke hal-hal opsional. Secara teori, semua hal yang membuatnya lebih mudah diurai untuk komputer harus meningkatkan keterbacaan keseluruhan bagi manusia juga, tapi itu jauh lebih bisa diperdebatkan.

Argumen ini menjadi sangat kuat ketika Anda mempertimbangkan semua pengubah tipe opsional yang dimiliki bahasa "non-modern" seperti C ++, seperti *untuk pointer, &untuk referensi const,, volatiledan sebagainya. Setelah Anda melemparkan koma untuk beberapa deklarasi, Anda mulai mendapatkan beberapa ambiguitas yang sangat aneh seperti int* a, b;tidak membuat bpointer.

Bahkan C ++ sekarang mendukung deklarasi "ketik di kanan" dalam bentuk auto x = int{ 4 };, dan itu memang memiliki beberapa keunggulan .

Ixrec
sumber
2
+1 untuk mengetahui keberadaan kata kunci wajib seperti varitu memberikan mayoritas penyederhanaan parsing.
8bittree
2
Bukankah keberadaan varkata kunci mengalahkan tujuan notasi nama-pertama? Saya tidak melihat keuntungan dari menulis var userInput: String = getUserInput()lebih String userInput = getUserInput(). Keuntungannya hanya akan relevan jika userInput = getUserInput()juga diizinkan, tetapi itu akan menyiratkan pernyataan implisit dari variabel scoped, yang menurut saya cukup menjijikkan.
kdb
2
@kdb dalam bahasa seperti C # dan C ++ , var userInput = getUserInput()atau auto userInput = getUserInput(), kompiler tahu akan getUserInput()kembali Stringsehingga Anda tidak harus menentukannya dengan jenis kata kunci pengurangan. userInput = getUserInput()tentu saja digunakan sebelum menyatakan.
Wes Toleman
32

Mengapa di hampir semua bahasa pemrograman modern (Go, Rust, Kotlin, Swift, Scala, Nim, bahkan versi terakhir Python) selalu muncul setelah deklarasi variabel, dan bukan sebelumnya?

Premis Anda cacat di dua bidang:

  • Ada bahasa pemrograman new-ish yang memiliki tipe sebelum pengenal. C♯, D, atau Ceylon, misalnya.
  • Memiliki tipe setelah pengenal bukan merupakan fenomena baru, itu kembali ke setidaknya Pascal (dirancang 1968-1969, dirilis pada 1970), tetapi sebenarnya digunakan dalam teori tipe matematika, yang dimulai ~ 1902. Itu juga digunakan dalam ML (1973), CLU (1974), Hope (1970-an), Modula-2 (1977–1985), Ada (1980), Miranda (1985), Caml (1985), Eiffel (1985), Oberon (1986), Modula-3 (1986–1988), dan Haskell (1989).

Pascal, ML, CLU, Modula-2, dan Miranda semuanya adalah bahasa yang sangat berpengaruh, sehingga tidak mengherankan bahwa gaya deklarasi tipe ini tetap populer.

Kenapa x: int = 42dan tidak int x = 42? Apakah yang terakhir tidak lebih mudah dibaca daripada yang pertama?

Keterbacaan adalah masalah keakraban. Secara pribadi, saya menemukan bahasa Mandarin tidak dapat dibaca, tetapi ternyata, orang-orang Tionghoa tidak. Setelah mempelajari Pascal di sekolah, mencoba-coba Eiffel, F♯, Haskell, dan Scala, dan setelah melihat TypeScript, Fortress, Go, Rust, Kotlin, Idris, Frege, Agda, ML, Ocaml,…, itu terlihat sangat alami bagi saya .

Apakah ini hanya tren atau adakah alasan yang benar-benar bermakna di balik solusi semacam itu?

Jika tren, itu cukup gigih: seperti yang saya sebutkan, itu kembali seratus tahun dalam matematika.

Salah satu keuntungan utama memiliki jenis setelah pengidentifikasi, adalah mudah untuk meninggalkan jenis jika Anda ingin disimpulkan. Jika deklarasi Anda terlihat seperti ini:

val i: Int = 10

Maka sepele untuk meninggalkan jenis dan disimpulkan seperti ini:

val i = 10

Sedangkan jika jenisnya datang sebelum pengenal seperti ini:

Int i = 10

Kemudian mulai menjadi sulit bagi parser untuk membedakan ekspresi dari deklarasi:

i = 10

Solusi yang biasanya muncul oleh perancang bahasa adalah dengan memperkenalkan kata kunci "Saya tidak ingin menulis jenis" yang harus ditulis alih-alih jenis:

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

Tapi ini sebenarnya tidak masuk akal: Anda pada dasarnya harus secara eksplisit menulis jenis yang mengatakan bahwa Anda tidak menulis jenis. Hah? Akan jauh lebih mudah dan lebih masuk akal untuk meninggalkannya, tetapi itu membuat tata bahasanya jauh lebih kompleks.

(Dan mari kita bahkan tidak berbicara tentang jenis pointer fungsi di C.)

Para perancang dari beberapa bahasa yang disebutkan di atas telah mempertimbangkan masalah ini:

  • Go FAQ (lihat juga: Go's Declaration Syntax ):

    Mengapa deklarasi mundur?

    Mereka hanya mundur jika Anda terbiasa dengan C. Dalam C, gagasannya adalah bahwa variabel dideklarasikan seperti ekspresi yang menunjukkan jenisnya, yang merupakan ide bagus, tetapi jenis dan tata bahasa ekspresi tidak tercampur dengan baik dan hasilnya bisa membingungkan; pertimbangkan fungsi pointer. Go kebanyakan memisahkan ekspresi dan tipe sintaks dan yang menyederhanakan hal-hal (menggunakan awalan *untuk pointer adalah pengecualian yang membuktikan aturan). Di C, deklarasi

    int* a, b;
    

    mendeklarasikan asebagai pointer tetapi tidak b; dalam Go

    var a, b *int
    

    mendeklarasikan keduanya sebagai pointer. Ini lebih jelas dan lebih teratur. Juga, :=formulir deklarasi pendek berpendapat bahwa deklarasi variabel penuh harus menyajikan urutan yang sama seperti :=itu

    var a uint64 = 1
    

    memiliki efek yang sama dengan

    a := uint64(1)
    

    Parsing juga disederhanakan dengan memiliki tata bahasa yang berbeda untuk tipe yang bukan hanya tata bahasa ekspresi; kata kunci seperti funcdan chanmenjaga semuanya tetap jelas.

  • FAQ Kotlin :

    Mengapa memiliki deklarasi ketik di sebelah kanan?

    Kami percaya itu membuat kode lebih mudah dibaca. Selain itu, ini memungkinkan beberapa fitur sintaksis yang bagus. Misalnya, mudah untuk menghilangkan anotasi jenis. Scala juga terbukti cukup baik ini bukan masalah.

  • Pemrograman dalam Scala :

    Penyimpangan utama dari Jawa menyangkut sintaks untuk anotasi tipe — ini " variable: Type" bukan " Type variable" di Jawa. Sintaks tipe postfix Scala menyerupai Pascal, Modula-2, atau Eiffel. Alasan utama untuk penyimpangan ini berkaitan dengan tipe inferensi, yang sering memungkinkan Anda menghilangkan tipe variabel atau tipe pengembalian metode. Menggunakan variable: Typesintaksis " " ini mudah — tinggalkan saja titik dua dan jenisnya. Tetapi dalam Type variablesintaks gaya C " " Anda tidak bisa begitu saja meninggalkan tipe — tidak akan ada penanda untuk memulai definisi lagi. Anda memerlukan beberapa kata kunci alternatif untuk menjadi pengganti untuk tipe yang hilang (C♯ 3.0, yang menggunakan beberapa inferensi tipe, digunakan varuntuk tujuan ini). Kata kunci alternatif semacam itu terasa lebih ad-hoc dan kurang teratur daripada pendekatan Scala.

Catatan: desainer Ceylon juga mendokumentasikan mengapa mereka menggunakan sintaks tipe awalan :

Awalan bukan anotasi jenis postfix

Mengapa Anda mengikuti C dan Java dalam menempatkan anotasi jenis terlebih dahulu, alih-alih Pascal dan ML dalam menempatkannya setelah nama deklarasi?

Karena kami pikir ini:

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

Jauh lebih mudah dibaca daripada ini:

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

Dan kami sama sekali tidak mengerti bagaimana orang bisa berpikir sebaliknya!

Secara pribadi, saya menemukan "argumen" mereka jauh kurang meyakinkan daripada yang lain.

Jörg W Mittag
sumber
4
Tampaknya kemampuan untuk meninggalkan jenis dan masih memiliki parsing sederhana diberikan oleh penambahan var, auto, let, atau sejenisnya, bukan oleh sisi mana variabel deklarasi tipe aktif. var Int x = 5atau bahkan Int var x = 5keduanya bisa disingkat var x = 5.
8bittree
5
Sementara saya suka itu sama-sama dapat dibaca setelah Anda terbiasa dan argumen tipe opsional kuat, saya ingin menunjukkan bahwa IDE tidak dapat secara otomatis mengusulkan nama variabel berdasarkan tipe tersebut. Saya rindu itu ketika saya menulis TypeScript.
Pierre Henry
"Your premise is flawed on two fronts" Semua itu lebih baru dari C # atau D.
Det
3
"Tapi ini sebenarnya tidak masuk akal: kamu pada dasarnya harus secara eksplisit menulis tipe yang mengatakan bahwa kamu tidak menulis tipe." Nah, versi type-on-the-right persis sama dalam contoh Anda ... Juga saya tidak setuju bahwa itu tidak masuk akal. Masuk akal persis seperti funcdi Go.
Sopel
4

Omong-omong, Pascal juga melakukannya, dan bukan bahasa baru. Itu adalah bahasa akademis, dirancang dari awal.

Saya akan mengatakan secara semantik lebih jelas untuk memulai dengan nama variabel. Jenisnya hanyalah detail teknis. Jika Anda ingin membaca kelas Anda seperti model kenyataan yang ada, masuk akal untuk menempatkan nama entitas Anda terlebih dahulu dan implementasi teknisnya yang terakhir.

C # dan java berasal dari C sehingga mereka harus menghormati "prior art", jadi tidak membingungkan programmer yang berpengalaman.

Martin Maat
sumber
3
Ada juga melakukannya dengan cara itu, tetapi Ada tentu saja bukan "bahasa akademik".
John R. Strohm
Ada melakukannya karena ia banyak mengutip dari Pascal dan Modula 2.
Gort the Robot
2
@StevenBurnap, sebuah pencarian cepat mengungkapkan bahwa buku pertama Wirth tentang Modula-2 keluar pada pertengahan 1982. Ada sedang dikembangkan beberapa tahun sebelumnya (DoD1 sekitar tahun 1977 atau lebih, seingat saya), dan distandarisasi, baik sebagai standar ANSI dan MIL-STD, pada tahun 1983. Keempat tim yang mengajukan kandidat untuk Ada mengambil PASCAL sebagai titik awal mereka. (Bell Labs diundang oleh DoD untuk menyerahkan bahasa kandidat berdasarkan pada C. Mereka menolak, mengatakan bahwa C tidak pada waktu itu dan tidak akan pernah cukup kuat untuk perangkat lunak mission-critical DoD.)
John R. Strohm
Saya pasti salah ingat. Saya pikir Wirth telah terlibat dalam Ada.
Gort the Robot
Pascal dimulai bahasa akademis, tetapi pada akhir tahun 90-an itu di ambang menjadi yang bahasa untuk menulis aplikasi Windows di via Delphi, yang sampai Borland melompat hiu dengan harga dan meninggalkan pintu terbuka untuk Java dan C # untuk datang dan curi hadiahnya. Anda masih akan melihat satu ton Delphi di luar sana di dunia aplikasi windows lama.
Shayne
1

Kenapa x: int = 42dan tidak int x = 42? Apakah yang terakhir tidak lebih mudah dibaca daripada yang pertama?

Dalam contoh sederhana seperti itu, tidak ada banyak perbedaan, tetapi mari kita buat sedikit lebih rumit: int* a, b;

Ini adalah deklarasi aktual dan valid dalam C, tetapi tidak melakukan apa yang seharusnya dilakukan secara intuitif. Sepertinya kita mendeklarasikan dua variabel tipe int*, tetapi sebenarnya kita mendeklarasikan satu int*dan satu int.

Jika bahasa Anda menempatkan tipe setelah nama variabel dalam deklarasi, masalah itu tidak mungkin terjadi.

Mason Wheeler
sumber
4
Saya tidak yakin mengapa memindahkan deklarasi tipe setelah akan mempengaruhi ini. Apakah x, y *int;dua *intatau satu *intdan satu int? Membuat int*atau *intsatu token alih-alih dua akan menyelesaikannya, atau menggunakan elemen sintaksis tambahan seperti :, yang dapat bekerja di kedua arah.
8bittree
@ 8bittree Setiap bahasa yang saya kenal yang menggunakan deklarasi nama-pertama menggunakan token tambahan, seperti :atau as, antara nama dan jenisnya.
Mason Wheeler
2
Dan di Go, x, y *intberarti "mendeklarasikan dua variabel, x dan y, dengan tipe pointer-to-int".
Vatine
10
C # menggunakan sintaks awalan, namun memperlakukan int[] x, ysebagai dua array. Hanya sintaks konyol C yang memperlakukan pengubah tersebut sebagai operator unary pada variabel (dan campuran awalan dan postfix, tidak kurang) yang menyebabkan masalah.
CodesInChaos