- Dari pengalaman menulis perangkat lunak selama lebih dari 20 tahun, static typing yang kuat hampir selalu layak dipilih, kecuali untuk kasus seperti REPL atau skrip sekali pakai
- Tipe meninggalkan kontrak antara pemanggil dan yang dipanggil di dalam kode, sehingga parameter atau nilai balik yang salah dapat disaring saat kompilasi atau pemeriksaan tipe
- Contoh string
"20"dari input HTML yang dipakai seperti angka lalu menjadi"201"menunjukkan perbedaan antara error yang tertangkap sebelum runtime dan error yang terlihat oleh pelanggan - Svix berupaya memasukkan kunci Redis, nilai cache, identifier seperti
PersonId·PetId, dan validasi input API ke dalam sistem tipe untuk mengurangi typo dan pengiriman ID yang salah - Menghilangkan tipe bisa mempercepat implementasi awal, tetapi biaya dokumentasi, pengujian, dan debugging membesar; dengan type inference dan dukungan IDE, refactoring dan onboarding menjadi lebih mudah
Alasan bersikukuh pada static typing
- Static typing yang kuat bukan sekadar ide bagus; untuk sebagian besar perangkat lunak, ia mendekati default yang benar
- Bahasa atau varian tanpa tipe juga punya kegunaan
- Penggunaan REPL
- Skrip sekali pakai di lingkungan yang memang hampir tidak memiliki tipe, misalnya shell
- Di luar itu, dalam sebagian besar kasus lebih baik memilih tipe yang kuat
- Tanpa tipe, kecepatan pengembangan saat ini bisa meningkat, tetapi itu lebih mirip “melaju sekencang mungkin menuju tebing”
- Pada akhirnya pilihannya hanya salah satu dari dua hal
- Bekerja lebih banyak untuk memeriksa invariant saat kompilasi atau pemeriksaan tipe
- Bekerja lebih sedikit dan memeriksanya saat runtime, atau bahkan tidak memeriksanya sama sekali saat runtime
- Error runtime tidak selalu tertangkap selama pengembangan, dan sekalipun tertangkap, bisa terjadi dengan cara yang terlihat oleh pelanggan
- Pengujian memang membantu, tetapi sulit menguji semua kemungkinan tipe parameter fungsi yang salah; lebih mudah mencegah tipe yang salah dengan tipe
Tipe langsung terhubung dengan kontrak kode dan pengurangan bug
- Tipe adalah komentar kode yang berguna bagi manusia maupun tool, sekaligus perangkat yang membuat kontrak antarbagian kode menjadi lebih ketat
- Bahkan untuk fungsi ucapan ulang tahun yang sama, kejelasan kontraknya bisa sangat berbeda
birthdayGreeting1(...params)bahkan tidak menunjukkan jumlah parameter, sehingga perilakunya sulit diketahui tanpa membaca dokumentasibirthdayGreeting2(name, age)memberi petunjuk bahwa ada nama dan umur, tetapi tidak ada tipenyabirthdayGreeting3(name: string, age: number): stringmemasukkan tipe input dan nilai balik ke dalam kontrak
- Jika fungsi diubah agar memakai
age + 1, versi tanpa tipe akan bermasalah pada input string- Nilai dari input HTML selalu bisa berupa string
birthdayGreeting2("John", "20")mengembalikan"John will turn 201 next year!"- Versi bertipe mensyaratkan
ageberupa angka, sehingga pemanggilan yang salah gagal dikompilasi
- Kontrak antara pemanggil dan yang dipanggil makin penting saat codebase membesar
- Dampak pada pemanggil saat yang dipanggil berubah dapat diketahui
- Ini sangat penting ketika pemanggil dan yang dipanggil ditulis oleh orang berbeda, seperti pada library open source
- Tanpa kontrak seperti ini, sulit memahami sejauh mana sebuah perubahan berdampak
Manfaat dalam pengalaman pengembangan, refactoring, dan onboarding
- Informasi tipe dimanfaatkan IDE dan tool pengembangan untuk meningkatkan pengalaman pengembangan secara signifikan
- Saat menulis kode, kita bisa langsung tahu ketika ekspektasi keliru, sehingga beban kognitif berkurang
- Developer tidak perlu mengingat semua tipe variabel dan fungsi dalam konteks saat ini; compiler akan memberi tahu bagian yang tidak cocok
- Refactoring juga menjadi lebih mudah
- Saat mengubah implementasi fungsi, compiler dapat memberi tahu apakah asumsi di tempat lain ikut rusak
- Engineer baru juga lebih mudah beradaptasi dengan codebase atau library
- Mereka dapat mengikuti definisi tipe untuk memahami di mana tipe itu digunakan
- Jika diubah, error kompilasi muncul, sehingga lebih mudah bereksperimen
- Perbedaannya terlihat pada contoh fungsi yang menerima tipe
PersonbirthdayGreeting3(person: Person)membuat titik penggunaanPersonmudah ditemukan lewat IDE- Bahwa
birthdayGreeting2(person)tanpa tipe sebenarnya mengharapkanPersonbaru bisa diketahui dengan membaca seluruh codebase
- Dokumentasi bisa menutupi sebagian kekurangan, tetapi dokumentasi mudah usang, sedangkan tipe menjadi dokumentasi yang tertinggal di dalam kode itu sendiri
- Tipe bisa dipandang sebagai bentuk yang lebih kuat dari nama variabel yang berguna
Cara Svix memasukkan informasi ke sistem tipe
- Svix berupaya memasukkan sebanyak mungkin informasi ke dalam sistem tipe untuk mengurangi error yang bisa ditangkap saat kompilasi, sekaligus meningkatkan pengalaman pengembangan
- Redis pada dasarnya adalah protokol berbasis string dan tidak memiliki tipe bawaan, sehingga manfaat tipe bisa hilang di lapisan Redis
- Contoh cache sederhana memiliki dua bug
- Ada typo pada nama key, seperti
person-{id}danpreson-{id} - Kode mencoba memuat data orang sebagai tipe
Pet
- Ada typo pada nama key, seperti
- Untuk menghindari masalah seperti ini, Svix menerapkan dua hal
- Key diwajibkan sebagai tipe tertentu, bukan string biasa
- Key dan value dipasangkan secara paksa
- Misalnya, jika menggunakan key yang dibuat dengan
PersonCacheKey::new(id), kode yang mencoba menerima hasilcache.get(PersonCacheKey::new(id))sebagaiPetakan gagal dikompilasi - ID berupa
Stringsederhana juga mudah menimbulkan kesalahando_something(id: String)tidak jelas ID apa yang harus diterima- Bisa saja seseorang meneruskan
pet.ownerpadahal yang sebenarnya harus diteruskan adalahpet.id
- Svix memberi tipe terpisah untuk setiap ID
PersonId(String)PetId(String)ownerpadaPetadalahPersonId
- Validitas ID yang diterima dari API juga dihubungkan dengan pembuatan tipe
- Misalnya, pet ID berbentuk Ksuid setelah prefiks
pet_ PetIddibuat tidak dapat dibentuk tanpa validasi- Dengan cara ini, saat tidak menemukan pet di database dan mengembalikan
404 Not Found, kita bisa yakin bahwa format ID itu sendiri valid - ID yang tidak valid sudah ditangani di API handler sebagai
422atau400
- Misalnya, pet ID berbentuk Ksuid setelah prefiks
Argumen kontra dan peran tool
- Argumen kontra utama terhadap tipe adalah kecepatan pengembangan, learning curve dan kompleksitas tipe, serta effort dan boilerplate
- Prototyping tanpa tipe memang bisa lebih cepat
- Kode bisa dikomentari tanpa keluhan compiler
- Nilai yang salah bisa dimasukkan ke field sampai nilai yang benar ditentukan
- Namun ini dianggap sebagai technical debt yang agresif dan tidak perlu, yang biayanya akan dibayar berkali-kali saat debugging di lokal, test suite, dan production
- Learning curve memang ada, tetapi kebanyakan orang tidak perlu menjadi ahli tipe
- Ekspresi tipe yang sederhana saja sudah cukup untuk bekerja
- Jika buntu, cukup minta bantuan
- Developer sudah harus mempelajari banyak hal seperti coding, React, dan framework seperti Axum, sehingga beban belajar tipe dianggap dilebih-lebihkan
- Belajar tipe adalah biaya sekali bayar, sedangkan manfaat yang diperoleh dari bantuan tipe saat onboarding ke codebase tertentu lebih besar
- Tanpa tipe, dibutuhkan dokumentasi dan pengujian yang cukup banyak untuk mendapatkan stabilitas dasar
- Dokumentasi dan test bisa usang
- Menambahkan tipe yang benar dianggap membutuhkan effort yang lebih sedikit
- Pada bahasa tanpa type inference, pengetikan bisa merepotkan
- Contoh Java menimbulkan pengulangan seperti
Person person1 = newPerson(); - Di tulisan tersebut kemudian ditambahkan koreksi bahwa Java memiliki type inference
- Contoh Java menimbulkan pengulangan seperti
- Bahasa dengan type inference seperti Rust lebih ringkas, misalnya
let person1 = new_person(); - Untuk mendapatkan manfaat tipe, diperlukan editor kode atau IDE yang mendukung fitur code completion modern yang memahami bahasa tersebut
- Berbeda dengan perdebatan selera seperti
vimvsemacs, atau tab vs spasi, posisinya adalah bahwa tipe memberi manfaat besar dibanding biayanya sehingga sulit memahami alasan untuk tidak menggunakannya - Ada tulisan lanjutan, using the type system effectively
1 komentar
Opini Hacker News
Hal yang paling membuat frustrasi dalam diskusi ini adalah semuanya membahas bagaimana orang merasakan sesuatu, sementara bukti empirisnya kurang
Penelitian yang ada menganggap tidak ada perbedaan bermakna antara kedua pendekatan, dan selama belum ada penelitian baru, sulit untuk menyimpulkan bahwa sisi yang disukai masing-masing orang pasti benar
Secara pribadi saya suka bahasa bertipe, tetapi sistem tipe seperti TypeScript terasa kurang memadai. Karena tipe tidak bisa benar-benar digunakan saat runtime, bug runtime tetap tersisa, dan karena banyak logika runtime tidak bisa dienkode ke dalam sistem tipe, kasus-kasus yang mustahil tetap harus diperiksa secara manual
Jika sistem tipe hampir menghilangkan kebutuhan untuk memikirkan bug runtime, itu akan menjadi keunggulan yang luar biasa, tetapi kebanyakan bahasa tidak sampai ke level itu dan berhenti di titik tengah yang ambigu antara overhead dan sebagian manfaat
Alasan mengapa tidak ada perbedaan besar dalam jumlah bug atau kecepatan tampaknya karena pada akhirnya semuanya saling meniadakan. Tanpa jaring pengaman tipe, kita menulis lebih banyak tes; sebaliknya, jika terlalu percaya pada sistem tipe, jumlah bug runtime yang tersisa akhirnya mirip. Saya berharap ada penelitian yang kokoh tentang topik ini, tetapi ini memang masalah yang sulit
Intinya lebih dekat pada alasan subjektif untuk menilai bahwa tipe tidak layak diinvestasikan
Beberapa tahun lalu saya meninjau penelitian tentang produktivitas pengembang, dan hampir semuanya buruk atau hanya benar-benar berlaku untuk junior. Misalnya, pemula mendapat manfaat besar dari umpan balik cepat atas error statis
Hampir mustahil menerapkan desain eksperimen yang baik kepada profesional, bukan mahasiswa, dan sulit mengekstrak sinyal karena harus memisahkan begitu banyak variabel seperti perbedaan individu, jenis pengembangan, dan cara manajemen. Menyedihkan, tetapi banyak hal dalam hidup sulit diukur secara efektif
Artikel dan banyak komentar membahas kenyamanan programmer, produktivitas, dan “ketepatan”, tetapi penelitian saat ini tidak menunjukkan hasil bermakna bahwa tipe statis memperbaiki atau memperburuk hal-hal ini. Pada dasarnya ini subjektif
Namun ada satu efek yang benar-benar diberikan tipe statis dan bisa dibuktikan secara sederhana: ia memungkinkan kita menulis kode yang lebih efisien. Dalam diskusi disiplin tipe, inilah yang semestinya menjadi pusatnya, sementara sisanya pada tahap ini lebih dekat ke angan-angan
TypeScript yang disebut dalam artikel sebenarnya bukan tipe kuat; ia bertipe statis, tetapi bertipe lemah. Tipenya lebih mirip anotasi, sehingga tidak ada jaminan performa atau tata letak memori. Jadi selain dokumentasi, kita membayar biaya tipe statis tetapi hampir tidak mendapatkan manfaat substansial
Mengejutkan bahwa komunitas teknologi mengabaikan bukti nyata dan menerima preferensi kultural maupun pribadi seolah-olah itu fakta
Seperti teknik lain, ia punya lubang, jadi untuk keandalan maksimal kita harus menggabungkan beberapa teknik. Membuang tipe statis karena tidak bisa menangkap semuanya mirip dengan tidak mengunci pintu karena pencuri bisa memecahkan jendela. Jika keamanan benar-benar penting, kunci pintunya dan pasang jeruji di jendelanya; bukan memilih salah satu saja
Jika sebuah bahasa cukup kuat untuk menulis program umum, ia juga cukup kuat untuk membuat bug
Tipe statis bisa efektif menangkap jenis bug tertentu, tetapi tidak semuanya. Kadang-kadang ia meningkatkan keterbacaan, seperti unit test statis atau bahasa khusus domain untuk dokumentasi yang dapat dijalankan
Secara umum, bahasa dinamis lebih gesit dan memungkinkan kita menulis lebih banyak tes dengan lebih mudah. Memang ada tes yang tidak perlu ditulis jika menggunakan bahasa bertipe statis, jadi tipe tetap berguna, tetapi tidak sekuat secara universal seperti yang sering dipercaya
Terlepas dari tekanan sosial bahwa kita harus menyukai static typing, alasan saya akhirnya menjauh dari static typing selalu karena ada menara gading yang dibangun di sekelilingnya
Saya telah membangun software masing-masing selama 10 tahun di kedua paradigma, dan sekarang saya lebih memilih tidak memakai type system
Dynamic typing terasa seperti kekuatan pemaksa yang membuat kita menulis kode sederhana, seperti unit test memaksa kode yang dapat dikomposisikan. Maksudnya kode yang mudah dibaca dan mudah dipahami
Saya juga kurang setuju dengan klaim bahwa developer pemula jadi lebih mudah mendekati codebase. Karena itu mudah mendorong loop berulang untuk sekadar menghilangkan tanda merah tanpa pemahaman. Type system membuat kita mempelajari satu lagi bahasa yang sangat domain-specific di atas bahasa pemrograman untuk tiap proyek, dan sering kali menghalangi pemahaman atas perilaku sebenarnya
Masalah-masalah yang disajikan dalam artikel bisa diselesaikan dengan cara yang sama kokohnya dengan tipe, sekaligus lebih mudah dipahami. Tipe memang bisa dipakai secara sederhana, tetapi dalam pengalaman saya, kenyataannya hampir tidak pernah begitu. Saya juga tidak suka autocomplete, jadi anggap saja begitu
Mungkin saya hanya developer tua yang meneriakkan “kode adalah dokumentasi”, tetapi mungkin juga ini pikiran yang lahir dari ketidakpuasan mendalam terhadap para developer yang belakangan membanjiri industri, tipe “ChatGPT bilang ini benar dan gajinya besar”
Namun secara umum, bukti yang berlawanan tampak lebih kuat. Tipe yang dibuat belakangan untuk mendokumentasikan kode dinamis nyata sering jauh lebih rumit daripada fitur yang sama jika diimplementasikan dengan static typing sejak awal. Ekosistem DefinitelyTyped di TypeScript menyediakan banyak sekali contoh
Sulit mengatakan tipe-tipe itu “digunakan secara sederhana”, tetapi kompleksitasnya bukan berasal dari type system itu sendiri atau cara penyediaan definisi tipe, melainkan dari kompleksitas kode dinamis yang dijelaskannya
Paket yang ekuivalen tetapi dibuat dengan static typing sejak awal biasanya punya antarmuka yang lebih sederhana. Karena tipenya didefinisikan di depan, bukan dipaksakan belakangan ke API yang sudah ada
Saya bahkan berpendapat bahwa tanpa menuliskan antarmuka secara eksplisit, kita tidak bisa tahu apakah antarmuka itu sederhana atau rumit. Saya setuju dengan ideal “kode adalah dokumentasi”, tetapi jika tidak ada kode yang menuliskan antarmuka itu secara eksplisit, maka menurut definisi antarmuka tersebut kurang terdokumentasi
Sampai-sampai saya ingin melihat langsung situasi seperti itu dari samping. Di bidang saya, logika domain-specific dalam codebase dynamic typing hampir mustahil dipahami, sedangkan kode static typing mengajari developer tentang business logic
Ungkapan “kode adalah dokumentasi” juga malah membingungkan. Dalam pengalaman saya, kode baru menjadi dokumentasi kalau ada static typing. Tanpa itu, tidak ada cara mengetahui objek punya atribut apa, atau mengapa kita memeriksa atribut yang kita kira tidak ada. Komentar memang ada, tetapi saya hampir tidak pernah melihat orang meninggalkan komentar yang bermakna
Pengalaman saya kebalikannya. Pola yang sangat dinamis sulit diberi tipe dengan benar, dan type system yang bagus mendorong pola yang lebih sederhana sehingga tipenya juga menjadi sederhana
Menghilangkan tanda merah itu penting. Tanda merah berarti ada masalah, dan itu jauh lebih mudah daripada menemukan error dengan cara lain. Saya tidak mengerti mengapa orang ingin menemukan error itu belakangan
Pernyataan bahwa Anda juga tidak suka autocomplete membuat saya condong tidak percaya pada penentang static typing. Programmer yang tidak ingin komputer membantu pemrograman terasa sangat mencurigakan
Namun itu tidak mengurangi keunggulan teknisnya. Sebuah teknologi bisa saja bagus, sementara orang-orang di sekitarnya penuh gaya sok pintar
Klaim bahwa dynamic typing membuat orang menulis kode sederhana terdengar seperti “mengemudi dengan penutup mata itu bagus karena membuat kita mengemudi pelan-pelan”. Kalau itu tujuannya, pakai saja linter yang membatasi panjang baris atau jumlah parameter; tidak perlu membuat batasan secara tidak langsung
Tipe bukan satu-satunya solusi, tetapi efek dibanding investasinya sangat besar, jadi menurut saya itu alat yang layak dikeluarkan lebih dulu. Investasinya nyaris tidak ada, manfaatnya besar
Saya setuju dengan “kode adalah dokumentasi”, tetapi tipe juga bagian dari kode. Jadi saya ingin menyatakannya sebagai “kode adalah dokumentasi, dan tipe adalah bagian dari kode”
Bidang kita adalah rekayasa, tidak ada satu jawaban benar, dan semuanya adalah trade-off. Justru karena itulah pekerjaan kita tidak langsung terotomatisasi lalu hilang
Nuansa “memandang rendah” orang lain karena opini atau pengalaman engineer lain di thread ini benar-benar tidak menyenangkan
Dalam situasi ketika sebagian besar data hilir-mudik melalui jaringan sebagai JSON, pertarungan untuk menerapkan strong static typing umumnya berlangsung tidak konsisten
Kita memang harus memakai semua alat yang memungkinkan, tetapi sebagian besar “data” jauh lebih lunak daripada yang dibayangkan. Orang menyimpan nomor telepon sebagai string bukan karena malas, melainkan karena mereka pernah keliru mengira bisa membuatnya menjadi tipe yang lebih kuat, lalu terlalu sering kena masalah. Nama, alamat, dan kode pos juga sama
Nilai-nilai seperti ini harus diterima dari pengguna, dan pada dasarnya tidak ada cara selain mem-parse teks. Jika Anda membangun sistem agar tidak menyimpan teks mentah sebelum parsing, suatu hari Anda hampir pasti akan menyesal
Menurut saya yang terbaik adalah layer yang menyimpan teks input asli sekaligus menyediakannya kepada pengguna backend sebagai kumpulan data bertipe, tetapi perlu dipertimbangkan apakah investasi sebesar itu sepadan di wilayah kecil masing-masing
Jika melakukan evaluasi berat, besar kemungkinan diperlukan layer yang mengubahnya menjadi SAT atau model numerik lain. Di dunia itu, angka adalah abstraksi. Jika mencoba cara lain, hampir pasti akan menderita. Sebaiknya ada layer yang menerjemahkan masalah menjadi formalisasi dan ruang solusi menjadi domain, dan tipe bisa membantu di sini, tetapi “tipe” yang benar-benar mendapat perhatian terlalu sering bukan tipe semacam ini
Berkat library seperti Serde dan Pydantic, kami mengikuti pendekatan bahwa deserialisasi adalah validasi. Semua data JSON divalidasi sebelum dijadikan struct di kode
Mirip dengan contoh Redis: meskipun menerima JSON lewat jaringan, setelah divalidasi sepenuhnya dan mencapai kode, kita bisa tenang bahwa itu adalah tipe yang terformat dengan baik. Jadi di dalam kode, kita bisa berasumsi bahwa tipe email adalah email yang valid, dan tipe ID adalah ID yang valid
Mencampur field nama dan field alamat hampir selalu merupakan error, dan type system bisa memaksakan itu
Jika typo berubah menjadi error runtime, itu bukan “bergerak lebih cepat”; dan jika saat mengubah signature fungsi Anda harus melakukan
greppada codebase untuk menemukan semua titik pemanggilan lalu berdoa semuanya sudah diperbaiki, itu juga bukan “lebih produktif”Tipe itu bagus, tetapi apa pun yang berlebihan akan jadi masalah. Jika Anda menjadikan pengodean seluruh logika bisnis ke dalam sistem tipe sebagai tujuan hidup, hasilnya bisa menjadi kekacauan yang lebih sulit dipahami daripada tidak memakai tipe sama sekali. Jika nama tipe dalam pesan error tidak muat dalam satu baris, berarti sudah terlalu jauh
Agar adil, itu dibuat untuk menyesuaikan dengan kode JS murni lama, dan variabel malang itu memang bisa berisi segala macam nilai
Saya akan selamanya berterima kasih pada TypeScript, tetapi tidak akan kaget kalau di masa depan kode seperti itu muncul dalam makalah tentang “era ketika tipe sudah kebablasan”
from pdb import set_trace: set_trace()Namun begitu program menjadi sedikit saja lebih kompleks, situasinya berubah. Saat Anda mulai mengirim data antar-sistem lewat queue, memakai async, thread, multiprocessing, dan menggunakan library biner terkompilasi di bagian yang kritis untuk performa, pada akhirnya Anda akan merasa seandainya semuanya ditulis dengan Erlang
Apakah ada metodologi menyeluruh seperti pengujian yang sangat ketat dengan cakupan kode 100%?
Jika titik pemanggilan tidak bisa ditangkap saat build atau compile, berarti Anda tidak sedang memakai static type
Typo bisa menghasilkan kode yang salah bahkan dalam bahasa dengan static type paling kuat. Kalau tidak, apa sebenarnya arti menulis kode? Mana yang lebih buruk: error runtime, atau hasil yang salah tanpa error?
Menjalankan proyek Python bisa saja lebih cepat daripada mengompilasi C++, dan bahasa dynamic type pun bisa menyediakan cara yang lebih baik daripada
grepuntuk menemukan pemanggilan fungsiMenurut pengalaman saya, klaim bahwa tidak memakai tipe punya keunggulan berupa pengembangan yang lebih cepat juga tidak benar. Static type membuat pemrograman sehari-hari lebih cepat
Saya sudah membahas belakangan bahwa IDE menjadi lebih baik berkat static type, tetapi saya juga merasakannya di REPL. Error tipe yang tertangkap secara statis memberikan pesan error yang bermakna dan jauh lebih dekat ke akar masalah sebenarnya, sehingga lebih cepat diperbaiki daripada error runtime
Ini juga mengurangi beban untuk berpikir terlalu hati-hati soal tipe. Karena compiler menjaga disiplin, saya bisa lebih sedikit memikirkannya. Saya bisa bergerak lebih cepat dengan keyakinan bahwa kategori besar error akan langsung tertangkap
Dalam pengalaman saya, sistem static type mudah digunakan, mempercepat pengembangan, dan meningkatkan keandalan. Sejauh ini biayanya hanya dua: bisa lebih sulit dipelajari, dan lebih sulit diimplementasikan
Developer junior yang direkrut 6 bulan kemudian akan jauh lebih lambat beradaptasi dengan kode tanpa tipe
Saya mengakui bahwa bagi sebagian orang penulisan awalnya mungkin “lebih cepat”, tetapi setelah itu semua developer yang membaca kode tersebut akan melambat
Daripada sekadar campur aduk yang lolos compiler, lebih baik memikirkan dengan tepat apa yang masuk dan keluar, serta alasannya
Menurut saya penulis salah di hampir semua poin. Saya juga berpikir seperti itu selama puluhan tahun, tetapi dalam beberapa tahun terakhir pandangan saya benar-benar berubah
Tipe mengurangi bug? Tidak. Mungkin sangat sedikit, tetapi tidak signifikan. Lihat saja riset terkait
Tipe memberi pengalaman pengembangan yang lebih baik? Tidak. REPL dan IDE saya punya semua definisi dan variabel. Semua autocomplete simbol, call tree, pencarian penggunaan, refactoring dengan percaya diri, serta menjalankan/mengganti/membungkus fungsi secara terpisah bisa saya lakukan di REPL dan di dalam aplikasi
Mengodekan semuanya ke dalam sistem tipe? Tidak mungkin. Perlu validasi runtime
Semoga beruntung juga saat harus mengurai definisi tipe ketika requirement berubah. Ini poin penentunya. Static type terlalu dini membekukan model data domain yang saat ini dipahami. Model itu akan berubah, dan jika kurang beruntung, Anda harus mendukung beberapa variasi model domain dalam runtime yang sama. Terutama jika memakai inheritance, penderitaannya lebih besar
Memang benar static type memberi leverage besar untuk optimisasi compiler, tetapi di antara bahasa dynamic type pun ada yang menyediakan static type sebagai fitur tambahan opsional
Untuk banyak use case, terutama pengembangan enterprise, bahasa fungsional yang mengutamakan immutability dengan dynamic type memberikan keuntungan besar dalam jangka panjang
Kesalahan kategori yang sering dilakukan pendukung strong type adalah mengasumsikan kode yang sama akan ditulis begitu saja tanpa tipe. Pada kenyataannya, orang tidak menulisnya seperti itu
Di hampir semua poin, kesimpulan saya justru kebalikannya. Pernyataan bahwa validasi runtime diperlukan tentu saja benar, tetapi sebagian besar validasi runtime bisa dihindari
Terkait perubahan requirement, saya justru melihat static type membuat adaptasi lebih mudah. Dalam sistem dynamic type yang saya alami, asumsi penting tentang struktur data tersebar di mana-mana; kadang diperiksa secara dinamis sebagai precondition/postcondition, kadang hanya ada di test, atau bahkan tidak diperiksa sama sekali
Untuk mengubah requirement, saya harus menalar semua dampaknya terhadap asumsi-asumsi implisit seperti ini, sehingga perubahan terasa menakutkan. Menjalankan aplikasi dengan kode baru memang mudah, tetapi sangat sulit mengetahui apakah kita telah merusak jalur kode langka yang tidak terpikirkan
Saya jauh lebih suka tahap analisis statis yang memberi tahu, “Anda mengubah interface ini; tahukah Anda bahwa jalur kode di sini bergantung pada bagian itu?” Static type bukan satu-satunya cara, tetapi menurut saya bebannya jauh lebih kecil daripada menyiapkan validasi dinamis dan test pada tingkat yang sama
Sistem tipe tidak menghilangkan validasi runtime, tetapi jika digunakan dengan benar, ia menguranginya secara dramatis
Saat requirement berubah, bagian terbaiknya adalah compiler memberi tahu secara persis apa yang perlu diperbaiki agar semuanya berjalan lagi. Jika melakukan hal yang sama di bahasa dinamis, Anda harus melacaknya sendiri, menunggu unit test gagal, dan berdoa tidak ada jalur yang terlewat
Saya bisa menemukan semua lokasi tempat tipe tertentu digunakan dengan jauh lebih percaya diri, lalu melihat apakah masing-masing perlu diubah. Dalam lingkungan dynamic type, pekerjaan ini jauh lebih remeh-temeh dan merepotkan
Sebagai developer yang pernah menulis ratusan ribu baris dalam C++, Python, dan JS, saya juga tidak begitu tahu. Tidak sejelas itu
Saya produktif di ketiganya, tetapi Python umumnya menang. Namun, saya tidak akan menulis game engine atau codec video dengan Python
JavaScript tidak konsisten dan aneh, tetapi warisan Netscape sudah telanjur mengikat kita semua ke sana
Dalam gaya yang sangat berorientasi objek dengan banyak kelas bersarang yang besar, tipe statis pada saat kompilasi/parsing dapat mengurangi banyak kesalahan. Namun, saya jadi melihat orientasi objek pada umumnya nyaris seperti bencana, dan fungsi sederhana serta data terstruktur hampir selalu menang dalam hal kesederhanaan dan kemudahan pemeliharaan
Language server dan IDE modern dapat menangkap banyak kesalahan pengetikan bahkan saat pengembangan JS/Python. Saya cukup rewel terhadap banyak aspek pemrograman, tetapi tidak punya posisi kuat soal tipe statis versus tipe dinamis. Keduanya punya jutaan proyek yang sukses
Menurut saya peningkatan produktivitas terbesar yang bisa diberikan sebuah bahasa adalah garbage collection. Saya lebih penasaran bagaimana perbandingannya dengan Go, yang punya sintaks dan tipe yang jauh lebih sederhana. Java juga bisa lebih baik. Meski verbose, untuk sebagian besar pekerjaan beban kognitifnya hanya sebagian kecil dari C++
Saya sering typo dan sering salah urutan argumen. Terutama saat mengerjakan machine learning, tipe sangat membantu. Hal yang paling ingin dihindari adalah setelah menghabiskan 30 menit untuk pemrosesan data, kode training kemudian mati
Menurut saya gradual typing di Python adalah titik tengah yang sangat baik antara prototyping cepat dan memberi anotasi tipe ketika sebuah fungsi sudah cukup matang
Perdebatan bahwa strong typing lebih baik daripada weak typing sudah selesai, tetapi apakah static typing lebih baik daripada dynamic typing belum selesai.
Pendukung static typing berpikir compiler harus memverifikasi invariant tipe untuk memastikan “kebenaran”, sementara pendukung dynamic typing menganggap itu buang-buang waktu.
Saya jelas berada di kubu yang terakhir. Sebab compiler hanya bisa memeriksa kebenaran tipe, bukan kebenaran program. Kebenaran tipe memang diperlukan untuk kebenaran program, tetapi tidak cukup. Pendukung static typing gagal mengakui hal ini, dan keliru mengira static typing menjamin lebih banyak hal daripada kenyataannya.
Lihat contoh
birthdayGreetingdi tulisan itu. Penulis senang karena static typing menangkap bug padabirthdayGreeting("John", "20"), sebab"20"bukan angka. NamunbirthdayGreeting(" ", 123)tidak tertangkap." "bukan nama.birthdayGreeting("Anna," -12335)juga tidak tertangkap. Sebaliknya,birthdayGreeting("Anna" 4.5)tertangkap, padahal 4,5 bisa saja dianggap sebagai usia, jadi justru bisa dibilang salah.Ini penting. “Bug tipe” sangat sepele untuk ditangkap, tetapi bug semantik bisa bersembunyi selama bertahun-tahun. Misalnya overflow pada saldo rekening yang disimpan sebagai
uint, angka yang seharusnya prima di posisi tertentu tetapi bukan, atau list yang tidak boleh kosong. Bahkan dependent type pun tidak bisa menjamin invariant seperti ini.Kalau sulit percaya, carilah bug besar yang menyebabkan ledakan wahana antariksa atau kecelakaan mobil. Sejauh yang saya tahu, tidak ada yang benar-benar disebabkan oleh kesalahan tipe; mayoritas besarnya adalah kesalahan semantik.
[1] Kebanyakan orang tidak memahami bahwa tipe harus dilihat setidaknya pada dua sumbu, strong/weak dan static/dynamic, sehingga terus mencampuradukkan weak typing dan dynamic typing. C itu static sekaligus weak typed, Python strong sekaligus dynamic typed, dan JavaScript weak sekaligus dynamic typed.
Justru karena itulah saya berada di pihak static typing. Karena terlalu sepele, hal itu bisa ditangani secara deklaratif, tepat di sebelah kode yang diperiksa, dengan umpan balik instan, di setiap call site dan setiap sub-ekspresi maupun statement.
Type annotation bukan berarti semantik atau logika domain sudah benar; itu tetap harus diuji. Namun ia bisa menggantikan puluhan pengujian sepele yang ortogonal terhadap logika yang kita pedulikan. Jujur saja, hampir tidak ada orang yang menulis semua pengujian seperti itu tanpa terlewat.
Di bagian belakang tulisan, saya mengatakan bahwa tipe divalidasi saat dibuat dari tempat seperti input pengguna. Jadi tipe
Nameselalu valid dan" "bukan nama. Karena tipe menjamin nama yang valid, di codebase kami hal itu pasti tertangkap.birthdayGreeting("Anna" 4.5)danbirthdayGreeting("Anna," -12335)sebenarnya valid di JS karenanumberadalah floating point. Namun saat menulis, saya membayangkan integer. Ini contoh lain bahwa tipe yang lebih ketat daripada TS, misalnya Rust, membantu mendefinisikan invariant dengan lebih baik.Singkatnya, saat mencoba menampilkan contoh sederhana, saya tidak mendefinisikan semua tipe seketat biasanya, dan akibatnya justru makin terlihat bug yang seharusnya bisa ditangkap oleh tipe.
Nameyang selalu merepresentasikan nama valid dan tipeAgeyang selalu merepresentasikan usia valid.Letakkan validasi di satu tempat, yaitu constructor tipe-tipe itu, lalu berbagai method seperti
birthdayGreetingbisa memakai nilai dari tipe itu tanpa harus memikul tanggung jawab tersebut.Saya tidak tahu cara menerapkan pola ini dengan baik tanpa type checking, atau setidaknya type hint opsional dan analisis statis. Sebagai gantinya, memvalidasi input di setiap method terlalu membebani, dan mengasumsikan caller memberikan nilai yang valid lalu mengujinya agar tidak terjadi kecelakaan besar juga tidak memuaskan.
birthdayGreetingmenerima rentang 1–150 mudah dilakukan di ADA.Ada juga beberapa masalah publik terkait wahana antariksa yang kemungkinan bisa ditangkap dengan type checking yang lebih baik. Konversi sistem metrik/imperial juga bisa dilakukan dengan memasukkan satuan ke dalam tipe. Namun untuk [2], kemungkinan besar masalahnya ada di sisi integration testing.
Tentu saja type checking tidak bisa menemukan semua masalah kode, terutama masalah algoritma, dan juga tidak menggantikan testing. Meski begitu, umpan balik instan dan type hint pada tahap pengembangan sangat berharga.
Dalam contoh itu, string nama juga bisa diubah menjadi tipe atau objek orang.
[1]: https://en.m.wikipedia.org/wiki/Ariane_flight_V88
[2]: https://en.m.wikipedia.org/wiki/Mars_Climate_Orbiter
Tidak ada solusi yang sempurna, tetapi ada banyak solusi yang bernilai.
Ada cukup banyak konvergensi dalam masalah ini. Kini sebagian besar bahasa menyediakan type inference sampai taraf tertentu pada level pernyataan. C++ juga punya
autoBerkat itu, boilerplate tipe dalam kode jauh berkurang. Masa ketika harus menuliskan seluruh tipe iterator yang panjang di pernyataan
forC++ sudah berlaluDeklarasi fungsi dan field struct adalah tempat informasi tipe dibutuhkan untuk membaca kode. Jika program melampaui beberapa ratus baris atau pengembangnya lebih dari satu orang, komentar sampai kadar tertentu menjadi wajib
Penolakan utama tentu datang dari pengguna Python dan JavaScript. Python belakangan menambahkan sistem tipe berbasis anjuran yang sangat aneh, dan JavaScript belakangan menambahkan TypeScript. Keduanya adalah sistem tipe yang ditempelkan belakangan, dan dipakai di lingkungan yang mencampur kode bertipe dan kode tanpa tipe. Ini menyakitkan
LISP juga puluhan tahun lalu menambahkan sistem tipe belakangan lewat “flavors” dan Common LISP Object System, dan hasilnya juga tidak sedap dipandang. Pelajarannya adalah bahwa menempelkan sistem tipe belakangan akan jadi berantakan
Ada fitur bagus seperti
Optionalyang memaksa pemeriksaan sebelum memakaiNone, atau structural subtyping melaluityping.Protocol. Akan lebih baik jika Python sejak awal dirancang dengan tipe dalam pikiran, tetapi jika mempertimbangkan tuntutan untuk terintegrasi dengan kode Python yang sudah ada dan tidak merusak kode apa pun, hasilnya cukup baikMasalah yang lebih besar dari static typing di Python adalah ekosistem dan konvensinya. Ini makin parah karena banyak pengembang pada dasarnya memakai Python sebagai data scientist. Karena malas menulis signature method yang benar, mereka menyalahgunakan
*args/**kwargsSangat umum method-method mengoper DataFrame atau dictionary seperti tas berisi macam-macam barang. Nilai tambah kalau method menambah atau menghapus kolom atau field sehingga Anda tidak tahu apa isi tas data itu sampai menjalankan kode atau membaca semua barisnya
Tentu saja hampir semua bahasa bisa melakukan hal serupa. Di C#, semua tipe bisa dibuat
dynamic, atau semua method Go bisa menerimainterface{}. Namun Python sudah lama secara aktif mendorong pendekatan seperti ini, dan bahkan sekarang banyak tutorial pemula memperkenalkan “kalau menerima*kwargs, Anda tidak perlu mengubah signature fungsi” bukan sebagai jebakan mengerikan, melainkan sebagai fitur tingkat lanjut untuk orang pintarIni esensial untuk migrasi bertahap, dan sepenuhnya bisa dipahami mengapa ia bekerja seperti itu
Flavors diperkenalkan ke Lisp yang belum memiliki sistem tipe, dan kemudian CLOS ditambahkan ke Common Lisp, yaitu Lisp yang sudah memiliki sistem tipe
Orang-orang selalu fanatik terhadap hal-hal yang mereka pegang secara emosional alih-alih rasional
Klaim bahwa “tipe mengurangi bug” lebih merupakan klaim yang terasa masuk akal daripada fakta
https://blog.metaobject.com/2014/06/the-safyness-of-static-t...
Bukan berarti belum cukup banyak percobaan. Klaim itu bahkan bisa dikatakan telah terbantahkan
Namun secara pribadi saya menyukai static typing. Terutama karena efek dokumentasi-nya, dan mungkin bukan kebetulan, satu-satunya efek positif yang memang punya dasar empiris kuat juga ada di sisi itu
Mencegah regresi bisa lebih penting daripada menulis kode yang benar sejak awal, dan ketika melihat kode yang tidak berevolusi, bagian itu tidak bisa dinilai
Secara konkret, menghapus field objek dalam proyek JavaScript murni yang besar pada dasarnya adalah ladang ranjau dan dulu telah menimbulkan banyak bug. Sebaliknya, dalam proyek TypeScript yang lengkap, perubahan yang sama bisa dilakukan dengan percaya diri
Saya pikir ini bisa dipandang sebagai bukti empiris bahwa static typing yang sangat kuat mengurangi bug
Pada akhirnya, seperti dikatakan banyak komentar, tipe bukan persoalan biner ya/tidak, melainkan spektrum besar dengan banyak sumbu seperti statis/dinamis, kuat/lemah, dan sebagainya. Ada perbedaan besar antar sistem tipe, dan ada pula perbedaan besar dalam cara orang menerapkan sistem tipe itu pada masalah
Bahkan dalam bahasa dengan tipe statis dan kuat, Anda bisa merepresentasikan semuanya sebagai string dan terus mengonversinya; itu pada dasarnya bekerja seperti bahasa bertipe dinamis. Sebaliknya, jika memanfaatkan alat yang diberikan sistem tipe untuk membuat class yang merepresentasikan nilai legal dan menegaskan invariant penting, Anda bisa mendapatkan manfaatnya
Produktivitas naik bukan beberapa kali lipat, melainkan beberapa orde besaran. Ini saya katakan sebagai seseorang yang banyak memakai berbagai bahasa yang mencakup rentang luas spektrum ini, seperti C, C++, Java, Python, JavaScript, TCL, dan lainnya
Kode yang belum lama disentuh—baik dalam proyek saat ini maupun dependency—menjadi jauh lebih mudah dinalar. Anda tidak perlu terus menyimpang ke sana-sini untuk mencari tahu persis apa yang bisa dilakukan dengan objek yang dikembalikan suatu fungsi, sehingga bisa lebih fokus pada masalah yang sedang dihadapi
Ada juga rasa lega yang menyenangkan ketika kompilasi lolos, tetapi itu sekunder
Tidak ada keterikatan emosional selain kemarahan saat menghadapi “tapi itu kan tidak menghilangkan semua bug!”
Perdebatan ini terasa persis seperti itu
Saya hanya menginginkan rasionalitas sederhana ketika compiler mengatakan “tidak boleh” saat saya mencoba memperlakukan hashmap seperti Apple atau String