3 poin oleh GN⁺ 2024-07-23 | 1 komentar | Bagikan ke WhatsApp

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

 
GN⁺ 2024-07-23
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

    • Contoh kode:
      if(!Whatever.TryParse<Thingy>(input, out var output)) output = some-sane-default;
      
    • Contoh kode:
      if(!Whatever.TryParse<Thingy>(input, out var output)) throw new ApplicationException($"Not a valid Thingy: {input}");
      
    • Disarankan untuk tidak menggunakan yang kedua di driver mode kernel
  • 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