1 poin oleh GN⁺ 4 jam lalu | 1 komentar | Bagikan ke WhatsApp
  • Alasan popularitas tipe statis menurun dari tahun 2000-an hingga awal 2010-an lalu meningkat lagi pada pertengahan hingga akhir 2010-an dijelaskan oleh peningkatan kualitas sistem tipe statis
  • Sistem tipe dinamis diibaratkan seperti menggali tanah dengan tangan kosong, karena manusia harus menilai sendiri status dan isi variabel serta field, sementara komputer tidak membantu maupun menghalangi
  • Sistem tipe statis lama seperti Java awal atau C++98 diibaratkan sebagai sekop kertas, karena bahkan tidak membantu membedakan pointer nullable dan non-nullable, serta memaksa penulisan nama tipe berulang-ulang
  • Sistem tipe modern seperti TypeScript, Haskell, MyPy, Swift, dan Rust lebih baik mendukung pemeriksaan kesalahan program dan representasi status melalui penanganan null, sum type·union type, dan type inference
  • Seiring fitur IDE seperti pelengkapan otomatis nama method menjadi umum, informasi yang dimasukkan ke dalam sistem tipe statis menghasilkan keuntungan produktivitas selain pemeriksaan kesalahan

Klaim utama

  • Popularitas tipe statis dipandang meningkat lagi bukan sekadar karena tren, melainkan karena kualitas sistem tipe statis yang bisa digunakan secara luas telah membaik
  • Digunakan analogi bahwa jika ada sekop yang bagus, menggali tanah dengan sekop lebih baik daripada dengan tangan kosong, tetapi jika yang tersedia hanya sekop dari kertas, tangan kosong justru lebih baik
  • Dalam sistem tipe dinamis, manusia harus memikirkan sendiri status dan isi variabel serta field dalam program, dan komputer tidak membantu penilaian itu
  • Sistem tipe statis yang buruk bisa menjadi beban yang lebih besar daripada bantuan, dan ini diibaratkan seperti menggali tanah dengan sekop kertas

Keterbatasan sistem tipe statis di masa lalu

  • Sistem tipe statis pada Java awal atau C++98 yang banyak digunakan pada tahun 90-an dan awal 2000-an bahkan tidak mampu membantu dengan baik untuk tugas sederhana seperti membedakan pointer nullable dan non-nullable
  • Sistem tipe statis lama dijelaskan sebagai struktur yang tidak memiliki sum type dan hanya memiliki product type
  • Sistem tipe statis lama memaksa penulisan nama tipe secara manual di banyak tempat
  • Kode seperti BufferedReader bufferedReader = new BufferedReader(new FileReader(filename)); digambarkan sebagai bencana kecil

Perbaikan pada sistem tipe modern

  • Sistem tipe modern seperti TypeScript, Haskell, MyPy, Swift, dan Rust menyediakan cara untuk membedakan tipe nullable dan tipe non-nullable
  • Dicontohkan Haskell dengan Maybe t, TypeScript dengan T | null, Swift dengan T?, dan Rust dengan Optional<T>, sehingga sistem tipe dapat menunjukkan lokasi yang membutuhkan pemeriksaan null dan apakah ada yang terlewat
  • Dijelaskan bahwa dalam praktiknya, kita jadi hampir tidak pernah lagi melihat error null pointer saat runtime
  • Sistem tipe modern menyediakan setidaknya salah satu dari sum type atau union type, dan memungkinkan penerapan "Make invalid states unrepresentable"
  • Pendekatan ini memungkinkan objek yang merepresentasikan state machine mengekspresikan agar setiap field hanya ada ketika berada pada status yang relevan
  • Sistem tipe modern menyediakan type inference, sehingga jika compiler dapat menilai let x = 5; sebagai angka, tidak perlu menulis let x: number = 5;

Fitur IDE dan kesimpulan

  • Seiring fitur IDE seperti pelengkapan otomatis nama method makin umum, kegunaan sistem tipe statis menjadi lebih besar
  • Pada tahun 90-an, Intellisense adalah fitur inti Visual Studio, tetapi pada tahun 2020-an, fitur serupa tersedia di hampir semua IDE dan editor
  • Informasi yang dimasukkan ke dalam sistem tipe statis tidak hanya menghasilkan pemeriksaan kesalahan program, tetapi juga keuntungan produktivitas tambahan
  • Sistem tipe dinamis yang baik lebih baik daripada sistem tipe statis yang buruk, tetapi sekarang kita dapat menggunakan sistem tipe statis yang jauh lebih baik daripada di masa lalu

1 komentar

 
GN⁺ 4 jam lalu
Opini di Lobste.rs
  • Tulisan ini bagus, tetapi saya tidak sepenuhnya setuju. Walaupun sistem tipe statis di awal 2000-an tidak hebat, menurut saya itu jauh lebih baik daripada tidak punya tipe statis sama sekali
    Memang belum ada closed sum type, tetapi banyak hal masih bisa dimodelkan lewat subtipe, dan walaupun belum ada non-nullable type, referensi serta tipe non-pointer di C++, dan tipe primitif di Java, mengambil sebagian peran itu. Di Ruby atau JavaScript, semua tipe bukan hanya nullable, tetapi juga bisa diperlakukan seperti string, seperti integer, dan seperti semua tipe lain dalam program, jadi situasinya lebih buruk
    Menurut saya, alasan besar mengapa arus pendapat soal tipe statis berubah adalah karena saat ledakan jejaring sosial Web 2.0, keunggulan sebagai yang pertama masuk pasar lebih penting daripada segalanya. Meskipun harus menumpuk utang teknis dengan Ruby atau Python, merilis cepat dan beriterasi tetap lebih baik daripada tersingkir seperti Friendster atau Digg, dan kalau lambat, saat itu tinggal membeli lebih banyak server dengan dana bunga rendah yang mudah didapat
    Setelah itu datang ledakan mobile, di mana perangkat lunak harus berjalan di perangkat pengguna yang terbatas dan tidak bisa dikendalikan, aplikasi bertipe dinamis yang lambat memang benar-benar lambat, dan kalau terjadi type error tidak bisa dipulihkan dengan elegan lewat top-level response handler seperti di server. Dalam lingkungan seperti itu, keamanan dan performa dari tipe statis jadi jauh lebih meyakinkan

    • Ada cukup banyak makalah yang membandingkan Java dan C++ gaya 90-an dengan codebase bahasa bertipe dinamis, lalu menemukan tingkat bug yang mirip, dan para pendukung bahasa dinamis sering mengangkat ini sebagai bukti bahwa tipe statis tidak berguna
      Di awal 2000-an saya juga setuju, karena saat itu sistem tipe sering memaksakan sifat-sifat yang nyaris tidak pernah salah sambil memberi batasan yang tidak membantu penataan kode. Khususnya, cara subtipe digabungkan dengan pewarisan implementasi terasa tidak fleksibel
      Setelah memakai sistem tipe yang lebih modern, pandangan saya berubah. Di snmalloc, mesin status kepemilikan memori dipaksakan lewat sistem tipe C++, dan di codebase lain sistem tipe memeriksa perilaku overflow yang benar untuk penghitung ring buffer. Keduanya kalau salah merepotkan untuk di-debug dan merupakan sumber kesalahan yang umum, dan sistem ini benar-benar menggagalkan kompilasi pada kode yang tadinya saya kira benar, sehingga bug tidak sempat masuk ke tree
    • Menurut saya, mengembangkan dengan bahasa bertipe dinamis lebih lambat daripada dengan bahasa bertipe statis. Saya terus melihat klaim sebaliknya, tetapi saya tidak mengerti
      Di IDE, menekan . lalu mengetik sedikit nama metode dan menekan Enter pada kandidat yang benar menghemat 2 detik setiap beberapa detik, dan juga menghemat 30 detik yang biasanya dipakai untuk mencari definisi kelas saat tidak tahu metode apa saja yang ada. Prinsip ini juga tergambar dengan baik di https://grugbrain.dev/#grug-on-type-systems
      Kita jauh lebih sering menulis baris kode yang memanggil metode daripada menulis tipe parameter fungsi, jadi pertukaran ini sangat merugikan tipe dinamis. Hal yang sebenarnya bernilai bukanlah membolehkan kode absurd yang akan gagal saat runtime, melainkan menghilangkan keharusan menuliskan tipe variabel lokal, dan sejak awal bahasa bertipe statis tidak perlu melarang itu
    • Sistem tipe populer di awal 2000-an bukan cuma “tidak terlalu hebat”, tetapi memang buruk, dan sangat bertele-tele
      Codebase langka yang benar-benar memakai sistem tipe secara serius menumpuk halaman demi halaman kode yang tidak banyak mengatakan apa-apa, tetap dipenuhi kondisi runtime, dan pada Java, program juga benar-benar melambat seiring bertambahnya hierarki tipe. Sebagian besar codebase memakai tipe secara tambal sulam sambil menambahkan banyak kondisi runtime, dan dari sisi cakupan pengujian yang dibutuhkan, penghematannya tidak jauh berbeda dibanding sistem tipe dinamis
      Bahasa bertipe dinamis memang tidak memberi kompensasi statis, tetapi ringkas sehingga lebih mudah dibaca, ditinjau, dan diuji. Ini особенно benar dalam lingkungan seperti framework dependency injection akhir 90-an hingga awal 2000-an, saat menambahkan layanan baru berarti harus mengubah beberapa berkas XML. Orang juga bisa bekerja tanpa IDE yang memakan setengah RAM
      Karier awal saya persis seperti ini, jadi saya sepenuhnya setuju dengan tulisan tersebut. Rasio biaya-manfaat Java 1.4 sampai Java 6 begitu buruk sampai-sampai saya nyaris menyerah pada bahasa bertipe statis, lalu beberapa tahun kemudian saya mencoba Haskell sebagai hobi dan baru sadar bahwa tipe statis sebenarnya bisa punya rasio biaya-manfaat yang masuk akal, dan masalahnya adalah Java. Esai “python is not java” juga menggambarkan masa gelap itu dengan baik
    • Subtipe berbasis pewarisan bahkan lebih lemah. Ia tidak memberi kegunaan pattern matching dengan pemeriksaan exhaustiveness, dan implementasinya terpecah ke banyak tempat
    • Penjelasan bahwa yang penting adalah meluncurkan situs lebih dulu daripada pesaing, menaruhnya di depan pengguna, lalu mengunci skala ekonomi, terdengar cukup akrab dengan situasi saat ini juga
  • Aku ragu apakah setelah tipe statis menjadi semangat zaman, kita benar-benar melihat keuntungan keandalan pada perangkat lunak kita
    Menurutku kelebihan tipe statis jauh lebih terletak pada umpan balik pengembangan yang instan dan berkurangnya kegagalan runtime yang fatal; secara teori kegagalan seperti itu selalu mungkin terjadi, tetapi dalam praktiknya rasanya tidak terlalu sering terjadi

    • Ya, pernah. Begitu kami mulai menargetkan 0 error TypeScript pada codebase TypeScript yang tidak kecil, upaya memanggil method pada undefined dan null menurun drastis
      Para junior dan beberapa senior awalnya skeptis dan mengira @ts-ignore akan bermunculan di mana-mana, tetapi kenyataannya hanya ada sekitar tiga, termasuk yang muncul karena tipe dependensi yang rusak. Dulu aplikasi di branch pengembangan kira-kira seminggu sekali mati karena kebingungan tipe dan menghambat pekerjaanku, sekarang aku bahkan tidak ingat kapan terakhir kali itu terjadi
      Hanya dengan memuaskan tsc, bug terkait tipe berkurang, termasuk saat aku sendiri tidak menulis kodenya. Sebaliknya, linter belakangan ini terlalu bersemangat, dan saat mencoba memuaskan alat seperti Sonar aku justru melihat kerusakan refactor yang nyata. 95% peringatannya palsu, 3% adalah bug pada alatnya, dan 2% yang membantu pun bukan penyebab bug yang sebenarnya. Alih-alih menghabiskan 1 minggu menyesuaikan codebase untuk menangkap satu bug, dalam prosesnya aku malah memasukkan dua bug lagi
      Pekerjaan untuk memuaskan tsc kira-kira menghasilkan 2 perbaikan bug murni dan 1 regresi per hari, tetapi regresinya biasanya tidak sampai crash total, melainkan salah perilaku dengan tingkat keparahan yang lebih rendah
      Menambahkan property-based testing ke sini rata-rata butuh 2~4 jam dan selalu mengungkap setidaknya satu bug. Jika kode bisa diuji dengan property-based testing, lakukanlah
      Dengan memperluas cakupan pengujian memakai model murah DeepSeek V4 Flash sambil berhati-hati agar tidak menghasilkan tes sampah, aku bisa memperbaiki sekitar 2~3 bug logika per hari, dan tidak ada crash. Hanya saja suite tesnya nyaris pada batas masih bisa dipelihara
      Ketika junior kubiarkan membuat tes ala kadarnya dengan keluarga Sonnet dan Opus 4.5, 4.6, model-model itu hanya membuat tes yang “mendokumentasikan perilaku saat ini”, jadi efek perbaikannya kecil, dan suite tesnya tidak bisa dipelihara sehingga harus dibuang
      Pengujian berbasis model sangat bagus untuk menangkap bug, tetapi penyiapannya rumit, dan sangat merepotkan untuk mengarahkannya agar menggali ke sudut-sudut alih-alih menghabiskan siklus pada fitur permukaan. Akan menarik kalau ada sesuatu seperti fuzzer berbasis model yang berbasis profil
      Singkatnya, type checker sangat bagus dalam menangkap kegagalan fatal dan berbagai kebingungan, dan property-based testing itu luar biasa. Tes biasa memerlukan banyak disiplin agar terus memberi hasil yang konsisten
    • Secara pribadi, menurutku iya. Pada JavaScript yang kupakai, bug null pointer setelah pindah ke TypeScript hampir menjadi sesuatu yang bisa diabaikan, dan rekan-rekanku juga mengalami hal serupa
  • Bagian yang paling sulit kusetujui di sini adalah mengelompokkan TypeScript bersama sistem tipe yang bagus

    • Betul. TypeScript tidak sound, dan khususnya cara ia mempersempit tipe melintasi await sudah beberapa kali menjebakku. Meski begitu, memang benar bahwa keadaannya membaik secara dramatis
      Sejujurnya, type system struktural pun akhirnya kuterima, dan menurutku itu akan berdampak positif pada desain bahasa ke depan
  • Klaim ini kurang meyakinkan. Bahasa pemrograman yang layak dengan algebraic data types dan inferensi tipe sudah ada sejak pertengahan 90-an
    Sistem tipe Java dan C++ memang sangat miskin, tetapi SML, OCaml, dan Haskell sudah ada dan rasanya tidak terlalu berbeda dengan sekarang. Jika orang-orang tidak memakai bahasa-bahasa itu, masalahnya adalah budaya, adopsi, dan kebutuhan yang tak terucapkan, bukan semata-mata karena “sistem tipe yang dapat dipakai belum cukup bagus”
    Atau jika klaimnya adalah “sistem tipe pada bahasa populer saat itu buruk, dan sistem tipe pada bahasa populer saat ini lebih baik sehingga sistem tipe jadi lebih populer”, itu terdengar seperti penalaran melingkar
    Ada juga banyak nuansa dalam perbedaan antara bahasa yang dirancang bersama sistem tipenya, dan bahasa yang awalnya dirancang tanpa tipe lalu belakangan ditempeli sistem tipe

  • Bahkan dari sudut pandang orang yang sejak awal lebih menyukai tipe dinamis, menurutku tulisan ini cukup adil. Sekarang aku bekerja dengan C#, untuk hobi aku memakai Lisp, dan dulu juga pernah memakai Python
    Saat harus memakai Java 5, aku kebanyakan terus-menerus bertarung dengan sistem tipenya karena keputusan buruk dari pengembang library. Setelah pindah ke C# sekitar 2010, sistem tipenya tidak secara aktif merugikan, tetapi umumnya terasa redundan, dan bahkan tidak bisa mencegah null pointer exception, yang merupakan kebingungan tipe paling umum di Python
    Sistem tipe C# baru benar-benar mulai membantu sekitar 2020, ketika non-nullable reference types diperkenalkan. Tahun ini native union type juga akan masuk, tetapi library union type yang memaksa exhaustiveness setidaknya sudah memungkinkan sejak 2016 dan aku mulai memakainya sejak 2020
    Menurutku tren tetap punya peran, tetapi sebagian darinya tidak buruk. Bahasa-bahasa yang sedang tren dengan sistem tipe yang lebih ekspresif telah membawa perbaikan juga ke bahasa-bahasa biasa yang kita pakai untuk bekerja dan dibayar

  • Haskell dan sistem tipenya sudah ada sejak era 2000-an. Memang tidak digunakan seluas sekarang, tetapi jelas memang sudah ada, jadi klaim ini perlu melengkapi bagian itu
    Secara pribadi, saya melihat TypeScript sebagai faktor besar yang membuat pengguna bahasa arus utama lebih akrab dengan sistem tipe yang lebih baik. Selain kualitas dan dukungan dari Microsoft, ada keunggulan bahwa ia diterapkan pada JavaScript, dan JavaScript lebih sangat membutuhkan tipe dibanding Python. Alasannya adalah “Undefined is not a function.” dan “The good parts.”

    • Akan bagus kalau ada buku “good parts” yang mengikuti JavaScript modern sambil tetap ringkas
      “Real World Haskell” terbit pada 2008, dan tujuannya adalah membuat Haskell terlihat lebih menarik bagi programmer arus utama. Saya tidak tahu seberapa besar itu membantu menyebarkan kabar baiknya
      Di dunia Java, Scala membawa tipe yang keren pada 2004, dan di .NET, F# muncul pada 2005. Scala mungkin mendapatkan pengguna menonjol paling banyak, seperti Twitter, tetapi posisinya tidak seperti TypeScript yang bisa menyerap porsi besar pengguna platform tersebut, dan juga tidak cukup menarik untuk menarik pengguna bahasa lain dalam jumlah besar seperti Rust atau Go
    • Tulisan itu sebenarnya sudah membahas masalah ini. Sistem tipe statis awal yang populer pada 90-an dan awal 2000-an, seperti Java awal atau C++98, diibaratkan sebagai sekop kertas
      Di paragraf berikutnya Haskell memang disebut sebagai “sistem tipe modern”, tetapi pada akhir 90-an dan awal 2000-an, orang yang punya pengalaman dengan Haskell, bahkan kalau menghitung yang hanya sekadar pernah menyentuhnya, secara praktis mendekati 0%. Tulisan itu membahas bagaimana mayoritas pengembang saat itu mengalami bahasa bertipe statis, dan mengapa mayoritas itu secara kolektif menghindari bahasa bertipe statis
    • Menurut saya, Haskell dan OCaml agak kesulitan karena ekosistem perkakas yang lemah. Bahasanya sendiri luar biasa, tetapi adopsinya hilang karena banyak luka kecil dari sisi tooling
      Misalnya, untuk memakai dune di OCaml, Anda harus memahami berkas opam, berkas dune, sintaks ocaml module, dan sintaks ocaml. Ekstensi kompilator opsional di Haskell juga terasa sama menakutkannya
      Ini kontras dengan cargo, di mana Anda cukup tahu toml dan Rust