Parse, jangan validasi
Hakikat desain yang dipandu tipe
- Slogan sederhana untuk menjelaskan desain yang dipandu tipe (type-driven design): parse, jangan validasi
- Slogan ini berarti memanfaatkan sistem tipe untuk meningkatkan keamanan dan ketepatan kode
Ranah kemungkinan
- Sistem tipe statis memudahkan kita menentukan apakah fungsi tertentu dapat diimplementasikan atau tidak
- Contoh:
foo :: Integer -> Void tidak mungkin diimplementasikan (Void tidak dapat memiliki nilai)
- Contoh: fungsi
head :: [a] -> a tidak terdefinisi ketika list kosong
Mengubah fungsi parsial menjadi fungsi total
Mengelola ekspektasi
- Fungsi
head tidak dapat mengembalikan nilai saat list kosong, sehingga tipe Maybe dapat digunakan agar ia bisa mengembalikan Nothing
- Namun, hal ini dapat menimbulkan ketidaknyamanan saat digunakan
Meneruskan ekspektasi
- Dengan menggunakan tipe
NonEmpty untuk merepresentasikan list yang tidak kosong, kita dapat menjamin bahwa fungsi head selalu mengembalikan nilai
- Dengan tipe
NonEmpty, pemeriksaan yang tidak perlu bisa dihapus, dan kesalahan dapat ditangkap saat kompilasi melalui sistem tipe
Kekuatan parse
- Perbedaan antara parse dan validasi terletak pada bagaimana informasi dipertahankan
- Fungsi
validateNonEmpty memvalidasi bahwa list tidak kosong, tetapi tidak mempertahankan informasi itu
- Fungsi
parseNonEmpty memvalidasi bahwa list tidak kosong dan mempertahankan informasi itu dalam tipe NonEmpty
Risiko validasi
- Pendekatan berbasis validasi dapat menimbulkan masalah yang disebut "shotgun parsing"
- Ini dapat menyebabkan situasi ketika program memproses sebagian input lalu baru menemukan bahwa sisa input tidak valid
- Parse membagi program menjadi dua tahap: tahap pertama memeriksa validitas input, dan tahap kedua hanya memproses input yang sudah valid
Parse dalam praktik
- Fokus pada tipe data dan buat type signature fungsi sespesifik mungkin
- Gunakan struktur data yang tidak bisa merepresentasikan status ilegal, dan ubah data menjadi representasi konkret sesegera mungkin
- Biarkan tipe data memandu kode, bukan kode yang mengendalikan tipe data
- Fungsi yang mengembalikan
m () harus digunakan dengan hati-hati
- Jangan takut melakukan parse data dalam beberapa tahap
- Hindari representasi data yang terdenormalisasi, dan jika perlu kelola dengan enkapsulasi
- Gunakan abstract data type yang membuat validator tampak seperti parser
Ringkasan, refleksi, dan bacaan terkait
- Memanfaatkan sistem tipe Haskell semaksimal mungkin tidaklah sulit, dan tidak perlu menggunakan ekstensi bahasa modern
- Gagasan intinya adalah "menulis fungsi total", yang sederhana tetapi mungkin sulit dipraktikkan
- Bacaan terkait yang direkomendasikan adalah tulisan blog Matt Parson "Type Safety Back and Forth" dan makalah Matt Noonan "Ghosts of Departed Proofs"
Ringkasan GN⁺
- Artikel ini menjelaskan cara memanfaatkan sistem tipe Haskell untuk meningkatkan keamanan dan ketepatan kode
- Artikel ini menekankan pentingnya memahami perbedaan antara parse dan validasi, serta pentingnya memeriksa validitas input melalui parse
- Penting untuk menggunakan struktur data yang tidak bisa merepresentasikan status ilegal melalui sistem tipe, dan mengubah data menjadi representasi konkret sesegera mungkin
- Sebagai bacaan terkait, direkomendasikan tulisan blog Matt Parson dan makalah Matt Noonan
1 komentar
Komentar Hacker News
Saran dan artikel ini sangat bermanfaat
Juga berguna bagi orang yang tidak menggunakan bahasa fungsional dengan pengetikan statis
Ide ini melampaui paradigma
Konsep serupa juga bisa ditemukan dalam literatur pemrograman berorientasi objek era 80–90-an, misalnya Design by Contract
TypeScript sering ditulis dengan cara mempersempit tipe saat runtime
Design by Contract kemungkinan memengaruhi spec di Clojure (Clojure adalah bahasa dinamis)
Pada dasarnya ini tentang asumsi dan jaminan (require dan provide)
Setelah asumsi diverifikasi dan jaminan diberikan, tidak perlu memeriksa ulang asumsi yang sama di bagian lain program
Bisa membingungkan saat melihat properti yang sudah dijamin dalam kode diperiksa lagi, dan ini membuat kode lebih sulit dipahami serta diperbaiki
Pola ini juga bekerja baik di C# modern dan juga menghemat ruang
Memanfaatkan sistem tipe yang kuat untuk membuat kasus kesalahan tidak bisa direpresentasikan adalah hal yang baik, dan ini membantu mengurangi bug perangkat lunak
Memang butuh lebih banyak waktu untuk memikirkan masalah dan mengikuti desainnya, tetapi dalam banyak kasus waktu itu sepadan
Slogan "Parse, don’t validate" merangkum desain berbasis tipe dengan baik
Secara pribadi, lebih baik "selalu lakukan validasi hanya di satu konstruktor", karena dengan begitu objek yang tidak valid sama sekali tidak akan pernah ada
Jika ingin memodifikasi objek, sebaiknya itu diimplementasikan dengan memanggil kembali konstruktor yang sama untuk membentuk state baru
Mengingatkan pada bagian 5 dari qmail, yang mencakup "jangan melakukan parsing" dan "ada antarmuka yang baik dan antarmuka pengguna"
Jika mengajar kelas pemrograman tingkat menengah, saya akan meminta mahasiswa menulis esai yang membandingkan dan mengontraskan saran-saran ini; masing-masing punya pelajaran yang bisa dipetik, dan pada awalnya mungkin tampak saling bertentangan
Materi terkait: Richard Feldman, "Making Impossible States Impossible"
Diskusi sebelumnya:
Sudah diteruskan ke Crowdstrike
Mengingatkan pada komentar seseorang saat demam XML pertengahan 2000-an, bahwa banyak organisasi memilih XML karena XML menyediakan parser
Meski menulis parser tidak sulit dan juga menyenangkan, saya tidak bisa memahami mengapa orang enggan menulis parser
Bertanya-tanya apakah ini bertentangan dengan pendapat bahwa kata kunci "required" di Protocol Buffers adalah kesalahan besar
Mungkin yang terbaik adalah memiliki parsing fleksibel yang tidak tervalidasi sekaligus fitur parsing yang tervalidasi