Saya membaca kode jauh lebih sering daripada saya menulis kode, dan saya berasumsi bahwa sebagian besar programmer yang mengerjakan perangkat lunak industri melakukan ini. Keuntungan dari inferensi tipe yang saya asumsikan adalah kurang verbositas dan kode yang kurang tertulis. Tetapi di sisi lain, jika Anda lebih sering membaca kode, Anda mungkin menginginkan kode yang bisa dibaca.
Compiler menyimpulkan tipe; ada algoritma lama untuk ini. Tetapi pertanyaan sebenarnya adalah mengapa saya, programmer, ingin menyimpulkan jenis variabel saya ketika saya membaca kode? Bukankah lebih cepat bagi siapa pun untuk hanya membaca jenis daripada memikirkan jenis apa yang ada?
Sunting: Sebagai kesimpulan saya mengerti mengapa ini berguna. Namun dalam kategori fitur bahasa, saya melihatnya di ember dengan kelebihan operator - berguna dalam beberapa kasus tetapi memengaruhi keterbacaan jika disalahgunakan.
sumber
Jawaban:
Mari kita lihat Java. Java tidak dapat memiliki variabel dengan tipe yang disimpulkan. Ini berarti saya sering harus mengeja jenisnya, meskipun sangat jelas bagi pembaca manusia apa jenisnya:
Dan terkadang menjengkelkan untuk mengeja seluruh tipe.
Pengetikan statis verbose ini menghalangi saya, sang programmer. Sebagian besar jenis anotasi adalah pengisi baris berulang, regurgiasi bebas konten dari apa yang sudah kita ketahui. Namun, saya suka mengetik statis, karena ini sangat membantu menemukan bug, jadi menggunakan pengetikan dinamis tidak selalu merupakan jawaban yang baik. Jenis inferensi adalah yang terbaik dari kedua dunia: Saya dapat menghilangkan jenis yang tidak relevan, tetapi masih yakin bahwa program saya (tipe-) memeriksa.
Meskipun inferensi tipe sangat berguna untuk variabel lokal, itu tidak boleh digunakan untuk API publik yang harus didokumentasikan dengan jelas. Dan kadang-kadang jenisnya sangat penting untuk memahami apa yang terjadi dalam kode. Dalam kasus seperti itu, akan bodoh jika mengandalkan inferensi tipe saja.
Ada banyak bahasa yang mendukung inferensi tipe. Sebagai contoh:
C ++. Kata
auto
kunci memicu inferensi jenis. Tanpa itu, mengeja jenis untuk lambdas atau untuk entri dalam wadah akan menjadi neraka.C #. Anda dapat mendeklarasikan variabel dengan
var
, yang memicu bentuk inferensi tipe terbatas. Itu masih mengelola sebagian besar kasus di mana Anda ingin mengetik inferensi. Di tempat-tempat tertentu Anda dapat meninggalkan jenis sepenuhnya (misalnya di lambdas).Haskell, dan bahasa apa pun dalam keluarga ML. Sementara rasa spesifik dari inferensi tipe yang digunakan di sini cukup kuat, Anda masih sering melihat anotasi tipe untuk fungsi, dan karena dua alasan: Yang pertama adalah dokumentasi, dan yang kedua adalah pemeriksaan bahwa inferensi tipe benar-benar menemukan tipe yang Anda harapkan. Jika ada perbedaan, kemungkinan ada beberapa jenis bug.
sumber
int
, bisa berupa tipe numerik apa pun termasuk genapchar
. Saya juga tidak mengerti mengapa Anda ingin mengeja seluruh tipe untukEntry
saat Anda cukup mengetikkan nama kelas dan biarkan IDE Anda melakukan impor yang diperlukan. Satu-satunya kasus ketika Anda harus mengeja seluruh nama adalah ketika Anda memiliki kelas dengan nama yang sama dalam paket Anda sendiri. Tapi menurut saya desainnya jelek.int
contoh, saya berpikir tentang (menurut pendapat saya perilaku yang cukup waras) dari sebagian besar bahasa yang menampilkan inferensi tipe. Mereka biasanya menyimpulkanint
atauInteger
atau apa pun namanya dalam bahasa itu. Keindahan tipe inferensi adalah bahwa selalu opsional; Anda masih dapat menentukan jenis yang berbeda jika Anda membutuhkannya. MengenaiEntry
contoh: poin bagus, saya akan menggantinya denganMap.Entry<Integer, Map<Integer, SomeObject<SomeObject, T>>>
. Java bahkan tidak memiliki alias alias :(colKey
jelas dan tidak relevan: kita hanya peduli bahwa itu cocok sebagai argumen keduadoSomethingWith
. Jika saya mengekstrak loop itu ke dalam fungsi yang menghasilkan Iterable of-(key1, key2, value)
triples, tanda tangan yang paling umum adalah<K1, K2, V> Iterable<TableEntry<K1, K2, V>> flattenTable(Map<K1, Map<K2, V>> table)
. Di dalam fungsi itu, tipe nyatacolKey
(Integer
, bukanK2
) sama sekali tidak relevan.View.OnClickListener listener = new View.OnClickListener()
. Anda masih akan tahu jenisnya meskipun programmer itu "malas" dan disingkat menjadivar listener = new View.OnClickListener
(jika ini mungkin). Jenis redundansi seperti ini biasa terjadi - saya tidak akan mengambil risiko perkiraan di sini - dan menghapusnya memang berasal dari pemikiran tentang pembaca masa depan. Setiap fitur bahasa harus digunakan dengan hati-hati, saya tidak mempertanyakan itu.Benar bahwa kode dibaca jauh lebih sering daripada yang tertulis. Namun, membaca juga membutuhkan waktu, dan dua layar kode lebih sulit untuk dinavigasi dan membaca dari satu layar kode, jadi kita perlu memprioritaskan untuk mengemas rasio informasi-upaya / usaha-membaca yang terbaik. Ini adalah prinsip umum UX: Terlalu banyak informasi sekaligus membanjiri dan sebenarnya menurunkan efektivitas antarmuka.
Dan itu adalah pengalaman saya bahwa sering kali , tipe yang tepat tidak penting. Tentunya Anda kadang-kadang ekspresi sarang:
x + y * z
,monkey.eat(bananas.get(i))
,factory.makeCar().drive()
. Masing-masing berisi sub-ekspresi yang mengevaluasi nilai yang jenisnya tidak ditulis. Namun mereka sangat jelas. Kami baik-baik saja dengan membiarkan tipe tidak dinyatakan karena cukup mudah untuk mengetahui dari konteksnya, dan menuliskannya akan lebih berbahaya daripada kebaikan (mengacaukan pemahaman tentang aliran data, mengambil layar yang berharga, dan ruang memori jangka pendek).Salah satu alasan untuk tidak menyatakan ekspresi seperti tidak ada hari esok adalah bahwa garis menjadi panjang dan aliran nilai menjadi tidak jelas. Memperkenalkan variabel sementara membantu dengan ini, itu memaksakan perintah dan memberi nama untuk hasil parsial. Namun, tidak semua yang mendapat manfaat dari aspek ini juga mendapat manfaat dari jenisnya yang dijabarkan:
Apakah penting apakah
user
objek entitas, integer, string, atau yang lainnya? Untuk sebagian besar tujuan, tidak, itu cukup untuk mengetahui bahwa itu mewakili pengguna, berasal dari permintaan HTTP, dan digunakan untuk mengambil nama untuk ditampilkan di sudut kanan bawah jawaban.Dan ketika itu tidak peduli, penulis bebas untuk menulis jenis. Ini adalah kebebasan yang harus digunakan secara bertanggung jawab, tetapi hal yang sama berlaku untuk semua hal lain yang dapat meningkatkan keterbacaan (nama variabel dan fungsi, pemformatan, desain API, ruang putih). Dan memang, konvensi dalam Haskell dan ML (di mana semuanya dapat disimpulkan tanpa usaha ekstra) adalah untuk menuliskan jenis fungsi fungsi non-lokal, dan juga variabel lokal dan fungsi kapan pun sesuai. Hanya pemula yang memungkinkan setiap jenis disimpulkan.
sumber
user
tidak masalah jika Anda mencoba untuk memperluas fungsi, karena menentukan apa yang dapat Anda lakukan denganuser
. Ini penting jika Anda ingin menambahkan beberapa pemeriksaan kewarasan (karena kerentanan keamanan, misalnya), atau lupa bahwa Anda benar-benar perlu melakukan sesuatu dengan pengguna selain hanya menampilkannya. Benar, jenis bacaan untuk ekspansi ini lebih jarang daripada hanya membaca kode, tetapi juga merupakan bagian penting dari pekerjaan kita.Saya pikir inferensi jenis cukup penting dan harus didukung dalam bahasa modern apa pun. Kita semua berkembang dalam IDE dan mereka dapat banyak membantu jika Anda ingin tahu jenis yang disimpulkan, hanya sedikit dari kita yang masuk
vi
. Pikirkan kode verbositas dan upacara di Jawa misalnya.Tapi Anda bisa mengatakan itu baik-baik saja IDE saya akan membantu saya, itu bisa menjadi poin yang valid. Namun, beberapa fitur tidak akan ada di sana tanpa bantuan tipe inferensi, tipe C # anonim misalnya.
Linq tidak akan sebagus sekarang tanpa bantuan tipe inferensi,
Select
misalnyaJenis anonim ini akan disimpulkan dengan rapi ke variabel.
Saya tidak suka ketik inferensi pada jenis kembali
Scala
karena saya pikir poin Anda berlaku di sini, harus jelas bagi kami apa fungsi kembali sehingga kami dapat menggunakan API lebih lancarsumber
Map<String,HashMap<String,String>>
? Tentu, jika Anda tidak menggunakan jenis, maka mengeja mereka memiliki sedikit manfaat.Table<User, File, String>
lebih informatif, dan ada manfaatnya menulisnya.Saya pikir jawaban untuk ini sangat sederhana: menghemat membaca dan menulis informasi yang berlebihan. Khususnya dalam bahasa berorientasi objek di mana Anda memiliki tipe di kedua sisi tanda sama dengan.
Yang juga memberi tahu Anda kapan Anda harus atau tidak menggunakannya - ketika informasi itu tidak berlebihan.
sumber
Misalkan seseorang melihat kode:
Jika
someBigLongGenericType
dapat diberikan dari jenis pengembaliansomeFactoryMethod
, seberapa besar kemungkinan seseorang yang membaca kode untuk memperhatikan jika jenis tidak cocok, dan seberapa mudah seseorang yang melihat perbedaan dapat mengenali apakah itu disengaja atau tidak?Dengan mengizinkan inferensi, suatu bahasa dapat menyarankan kepada seseorang yang membaca kode bahwa ketika jenis variabel secara eksplisit dinyatakan orang tersebut harus mencoba untuk menemukan alasannya. Ini pada gilirannya memungkinkan orang yang membaca kode untuk lebih memfokuskan upaya mereka. Sebaliknya, jika sebagian besar waktu ketika suatu jenis ditentukan, kebetulan persis sama dengan apa yang telah disimpulkan, maka seseorang yang membaca kode mungkin kurang cenderung memperhatikan waktu bahwa itu agak berbeda. .
sumber
Saya melihat bahwa sudah ada beberapa jawaban yang bagus. Beberapa di antaranya saya akan ulangi tetapi kadang-kadang Anda hanya ingin meletakkan sesuatu dengan kata-kata Anda sendiri. Saya akan berkomentar dengan beberapa contoh dari C ++ karena itu adalah bahasa yang paling saya kenal.
Yang penting tidak pernah tidak bijaksana. Ketik inferensi diperlukan untuk membuat fitur bahasa lainnya praktis. Dalam C ++ dimungkinkan untuk memiliki tipe yang tidak dapat dipisahkan.
C ++ 11 menambahkan lambdas yang juga tidak dapat disangkal.
Ketik inferensi juga mendukung templat.
Tetapi pertanyaan Anda adalah "mengapa saya, sang programmer, ingin menyimpulkan jenis variabel saya ketika saya membaca kode? Bukankah lebih cepat bagi siapa pun hanya untuk membaca jenisnya daripada memikirkan jenis apa yang ada?"
Ketik inferensi menghilangkan redundansi. Ketika datang untuk membaca kode, kadang-kadang mungkin lebih cepat dan lebih mudah untuk memiliki informasi yang berlebihan dalam kode tetapi redundansi dapat menutupi informasi yang berguna . Sebagai contoh:
Tidak memerlukan banyak keakraban dengan pustaka standar untuk seorang programmer C ++ untuk mengidentifikasi bahwa saya adalah seorang iterator dari
i = v.begin()
sehingga deklarasi tipe eksplisit memiliki nilai terbatas. Dengan kehadirannya itu mengaburkan detail yang lebih penting (seperti yangi
menunjuk ke awal vektor). Jawaban baik oleh @amon memberikan contoh yang lebih baik dari verbosity membayangi detail penting. Sebaliknya menggunakan inferensi tipe memberikan keunggulan lebih besar pada detail penting.Walaupun membaca kode itu penting, itu tidak cukup, pada titik tertentu Anda harus berhenti membaca dan mulai menulis kode baru. Redundansi dalam kode membuat modifikasi kode lebih lambat dan lebih sulit. Misalnya, saya memiliki fragmen kode berikut:
Dalam hal ini saya perlu mengubah tipe nilai vektor untuk mengubah kode menjadi dua kali lipat:
Dalam hal ini saya harus memodifikasi kode di dua tempat. Kontras dengan inferensi ketik di mana kode aslinya adalah:
Dan kode yang dimodifikasi:
Perhatikan bahwa sekarang saya hanya perlu mengubah satu baris kode. Ekstrapolasi ini ke program besar dan inferensi tipe dapat menyebarkan perubahan ke tipe jauh lebih cepat daripada yang Anda bisa dengan editor.
Redundansi dalam kode menciptakan kemungkinan bug. Setiap kali kode Anda bergantung pada dua informasi yang dijaga tetap sama, ada kemungkinan kesalahan. Misalnya, ada ketidakkonsistenan antara kedua jenis dalam pernyataan ini yang mungkin tidak dimaksudkan:
Redundansi membuat niat lebih sulit untuk dilihat. Dalam beberapa kasus, tipe inferensi dapat lebih mudah dibaca dan dipahami karena lebih sederhana daripada spesifikasi tipe eksplisit. Pertimbangkan fragmen kode:
Dalam kasus yang
sq(x)
mengembalikan suatuint
, tidak jelas apakahy
merupakanint
karena itu adalah jenis pengembaliansq(x)
atau karena sesuai dengan pernyataan yang digunakany
. Jika saya mengubah kode lain sehinggasq(x)
tidak lagi kembaliint
, tidak pasti dari baris itu saja apakah jenisy
harus diperbarui. Kontras dengan kode yang sama tetapi menggunakan tipe inferensi:Dalam hal ini maksudnya jelas,
y
harus jenis yang sama dengan yang dikembalikan olehsq(x)
. Ketika kode mengubah jenis pengembaliansq(x)
, jenisy
perubahan untuk mencocokkan secara otomatis.Dalam C ++ ada alasan kedua mengapa contoh di atas lebih sederhana dengan inferensi tipe, inferensi tipe tidak dapat memperkenalkan konversi tipe implisit. Jika jenis pengembaliannya
sq(x)
bukanint
, kompiler dengan diam-diam menyisipkan konversi implisit keint
. Jika tipe kembalinyasq(x)
adalah tipe kompleks yangoperator int()
ditentukan, pemanggilan fungsi tersembunyi ini mungkin rumit.sumber
typeof
tidak berguna oleh bahasa. Dan itu adalah defisit bahasa itu sendiri yang harus diperbaiki menurut pendapat saya.