1 poin oleh GN⁺ 2023-10-05 | 1 komentar | Bagikan ke WhatsApp
  • 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 dokumentasi
    • birthdayGreeting2(name, age) memberi petunjuk bahwa ada nama dan umur, tetapi tidak ada tipenya
    • birthdayGreeting3(name: string, age: number): string memasukkan 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 age berupa 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 Person
    • birthdayGreeting3(person: Person) membuat titik penggunaan Person mudah ditemukan lewat IDE
    • Bahwa birthdayGreeting2(person) tanpa tipe sebenarnya mengharapkan Person baru 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} dan preson-{id}
    • Kode mencoba memuat data orang sebagai tipe Pet
  • 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 hasil cache.get(PersonCacheKey::new(id)) sebagai Pet akan gagal dikompilasi
  • ID berupa String sederhana juga mudah menimbulkan kesalahan
    • do_something(id: String) tidak jelas ID apa yang harus diterima
    • Bisa saja seseorang meneruskan pet.owner padahal yang sebenarnya harus diteruskan adalah pet.id
  • Svix memberi tipe terpisah untuk setiap ID
    • PersonId(String)
    • PetId(String)
    • owner pada Pet adalah PersonId
  • Validitas ID yang diterima dari API juga dihubungkan dengan pembuatan tipe
    • Misalnya, pet ID berbentuk Ksuid setelah prefiks pet_
    • PetId dibuat 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 422 atau 400

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
  • 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 vim vs emacs, 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

 
GN⁺ 2023-10-05
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

    • Kebanyakan orang tampaknya akan setuju bahwa tipe mencegah banyak bug, dan penelitian semacam itu juga sudah muncul di thread ini
      Intinya lebih dekat pada alasan subjektif untuk menilai bahwa tipe tidak layak diinvestasikan
    • Pada akhirnya sepertinya kita harus puas dengan ini sebagai masalah penilaian
      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
    • Pendapat saya juga hampir sama
      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
    • Tipe statis hanyalah satu lapis keju Swiss untuk perangkat lunak yang lebih andal
      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
    • Tampaknya ada anggapan bahwa jika program ditulis dengan bahasa yang mengekspresikan tipe, “bug runtime” akan hilang secara ajaib
      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”

    • Jika dynamic typing benar-benar membuat kebanyakan developer menulis kode yang lebih sederhana, itu akan menjadi argumen yang sangat kuat
      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
    • Pernyataan “type system menghambat pemahaman developer pemula dan membuat mereka hanya menghilangkan tanda merah” terdengar justru kebalikannya saat pertama kali mendengarnya
      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
    • Hampir seluruh logika seperti ini terasa terbalik bagi saya, jadi sulit dipahami
      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
    • Poin bahwa menara gading dan tekanan sosial bisa membuat orang enggan mengadopsi teknologi tertentu adalah pengamatan yang bagus
      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”
    • Saya hampir menulis jawaban yang sama
      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

    • Saya penulisnya. Ada satu hal dalam cara kami di Svix yang hanya saya singgung sekilas dalam satu paragraf, padahal seharusnya saya jelaskan lebih lanjut
      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
    • Nama dan alamat, meskipun jika dilihat lebih dalam keduanya adalah string, tetap harus dipakai sebagai tipe terpisah, bukan sekadar string
      Mencampur field nama dan field alamat hampir selalu merupakan error, dan type system bisa memaksakan itu
    • Apakah fakta bahwa sebagian besar data lewat sebagai JSON benar-benar membuatnya sekacau itu? Dalam kasus tertentu, Map juga merupakan tipe yang sepenuhnya masuk akal
  • Jika typo berubah menjadi error runtime, itu bukan “bergerak lebih cepat”; dan jika saat mengubah signature fungsi Anda harus melakukan grep pada 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

    • Saya pernah melihat definisi tipe TypeScript yang gila-gilaan
      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”
    • Dulu saya berpikir berinteraksi dengan program yang sedang berjalan lebih baik daripada dengan compiler, karena bisa melakukan pengeditan interaktif lewat 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
    • Saya benar-benar penasaran bagaimana orang yang benar-benar mengerjakan perubahan signature fungsi dengan cara seperti itu bekerja
      Apakah ada metodologi menyeluruh seperti pengujian yang sangat ketat dengan cakupan kode 100%?
    • Setiap kali melihat keluhan seperti ini tentang static type, saya jadi bertanya-tanya bagaimana mereka merancang definisi tipe dan arsitekturnya sampai hal ini menjadi masalah
      Jika titik pemanggilan tidak bisa ditangkap saat build atau compile, berarti Anda tidak sedang memakai static type
    • Saya tidak begitu paham bagaimana dua hal itu terkait dengan perdebatan tipe
      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 grep untuk menemukan pemanggilan fungsi
  • Menurut 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

    • Sepenuhnya setuju. Argumen soal maintainability pun hampir selesai dengan ini
      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
    • Bisa juga dikatakan bahwa justru pemikiran yang hati-hati itulah yang membuat software lebih bersih dan lebih baik
      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

    • Pada akhirnya ini perdebatan yang pasti membuat frustrasi, karena semua orang menarik kesimpulan yang benar-benar berbeda dari pengalaman masing-masing
      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
    • Melihat definisi/variabel di REPL dan IDE serta melakukan autocomplete/refactoring memang bagus, tetapi itu hanya bekerja saat Anda benar-benar menjalankan kode yang ingin diperiksa. Dalam pengalaman saya, pendekatan itu tidak berskala dengan baik
      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
    • Bagi saya justru bagian ini jauh lebih mudah dengan static type
      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
    • Itu mungkin pengalaman Anda, tetapi bukan pengalaman saya. Saya pikir tidak ada jawaban benar yang cocok untuk semua orang. Pilih yang cocok untuk Anda, lalu lanjutkan
  • 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

    • Terlepas dari sistem tipenya, C++ secara keseluruhan adalah bahasa yang cukup keras untuk bisa produktif
      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++
    • Di Python saya terutama menulis kode dengan gaya fungsional, tetapi tipe tetap sangat membantu
      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
    • Anda mungkin akan menyukai video WWDC beberapa tahun lalu tentang protocol-oriented programming (Swift): https://www.youtube.com/watch?v=p3zo4ptMBiQ
  • 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 birthdayGreeting di tulisan itu. Penulis senang karena static typing menangkap bug pada birthdayGreeting("John", "20"), sebab "20" bukan angka. Namun birthdayGreeting(" ", 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.

    • Banyak dari contoh itu sebenarnya bisa ditangkap cukup dengan static typing, tergantung sistem tipenya. Namun yang lebih penting adalah bagian “bug tipe sangat sepele untuk ditangkap”.
      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.
    • Saya penulisnya. Menurut saya contoh-contoh ini bukan counterexample, malah menunjukkan poin saya.
      Di bagian belakang tulisan, saya mengatakan bahwa tipe divalidasi saat dibuat dari tempat seperti input pengguna. Jadi tipe Name selalu valid dan " " bukan nama. Karena tipe menjamin nama yang valid, di codebase kami hal itu pasti tertangkap.
      birthdayGreeting("Anna" 4.5) dan birthdayGreeting("Anna," -12335) sebenarnya valid di JS karena number adalah 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.
    • Alasan contoh-contoh ini menarik adalah, menurut saya yang dibutuhkan adalah tipe Name yang selalu merepresentasikan nama valid dan tipe Age yang selalu merepresentasikan usia valid.
      Letakkan validasi di satu tempat, yaitu constructor tipe-tipe itu, lalu berbagai method seperti birthdayGreeting bisa 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.
    • Begitu menyebut ledakan wahana antariksa, saya langsung teringat [1]. Insiden terkenal ini terkait dengan type checking yang memiliki rentang nilai yang diizinkan yang didefinisikan terlebih dahulu untuk sebagian tipe, dan itu juga sejalan dengan pendapatmu. Membuat birthdayGreeting menerima 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
    • Saya tidak mengerti mengapa fakta bahwa compiler hanya memeriksa kebenaran tipe, bukan kebenaran program, menjadi dasar untuk menyebutnya buang-buang waktu.
      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 auto
    Berkat itu, boilerplate tipe dalam kode jauh berkurang. Masa ketika harus menuliskan seluruh tipe iterator yang panjang di pernyataan for C++ sudah berlalu
    Deklarasi 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

    • Menurut saya sistem tipe Python cukup bagus jika mempertimbangkan situasinya
      Ada fitur bagus seperti Optional yang memaksa pemeriksaan sebelum memakai None, atau structural subtyping melalui typing.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 baik
      Masalah 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/**kwargs
      Sangat 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 menerima interface{}. 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 pintar
    • “Sistem tipe” Python dan TypeScript dirancang untuk pengenalan tipe secara bertahap di lingkungan non-greenfield
      Ini esensial untuk migrasi bertahap, dan sepenuhnya bisa dipahami mengapa ia bekerja seperti itu
    • Flavors dan CLOS bukan “sistem tipe”. Itu adalah “sistem objek”, dan juga tidak terlalu jelek
      Flavors diperkenalkan ke Lisp yang belum memiliki sistem tipe, dan kemudian CLOS ditambahkan ke Common Lisp, yaitu Lisp yang sudah memiliki sistem tipe
    • Sistem tipe TypeScript benar-benar luar biasa bagus. Saya berharap lebih banyak sistem tipe memiliki daya ekspresif sebesar itu
  • 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

    • Menurut saya pernyataan “tipe mengurangi bug saat mengubah codebase yang berumur panjang” tidak terlalu dapat diperdebatkan
      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
    • Karena ini klaim yang sangat kuat, ambil contoh ekstrem: bagian tengah CompCert yang diverifikasi secara formal: https://users.cs.utah.edu/~regehr/papers/pldi11-preprint.pdf
      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
    • Saya jauh lebih menyukai tipe statis karena strong static typing membuat kode menjadi self-documenting
      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
    • Argumen untuk static typing adalah bahwa karena tipe bersifat statis, ia membuat bug tipe menjadi mustahil
      Tidak ada keterikatan emosional selain kemarahan saat menghadapi “tapi itu kan tidak menghilangkan semua bug!”
    • Bagaimana perasaan Anda jika seseorang berargumen bahwa “argumen emosional bisa dibuat lebih cepat daripada argumen rasional”?
      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