Baiklah, aturan pertama penanganan kesalahan di Haskell: Jangan pernah gunakanerror
.
Hanya saja mengerikan dalam segala hal. Itu ada murni sebagai tindakan sejarah dan fakta bahwa Prelude menggunakannya mengerikan. Jangan gunakan itu.
Satu-satunya waktu yang bisa Anda gunakan adalah ketika ada sesuatu yang begitu mengerikan secara internal sehingga ada sesuatu yang salah dengan tatanan realitas, sehingga menghasilkan hasil dari program Anda.
Sekarang pertanyaannya menjadi Maybe
vs Either
. Maybe
cocok untuk sesuatu seperti head
, yang mungkin atau mungkin tidak mengembalikan nilai tetapi hanya ada satu alasan yang mungkin untuk gagal. Nothing
mengatakan sesuatu seperti "itu rusak, dan Anda sudah tahu mengapa". Beberapa akan mengatakan itu menunjukkan fungsi parsial.
Bentuk penanganan kesalahan yang paling kuat adalah Either
+ sebuah kesalahan ADT.
Sebagai contoh di salah satu penyusun hobi saya, saya punya sesuatu seperti
data CompilerError = ParserError ParserError
| TCError TCError
...
| ImpossibleError String
data ParserError = ParserError (Int, Int) String
data TCError = CouldntUnify Ty Ty
| MissingDefinition Name
| InfiniteType Ty
...
type ErrorM m = ExceptT CompilerError m -- from MTL
Sekarang saya mendefinisikan banyak jenis kesalahan, menaruhnya sehingga saya memiliki satu kesalahan tingkat atas yang mulia. Ini bisa menjadi kesalahan dari setiap tahap kompilasi atau ImpossibleError
, yang menandakan bug kompilator.
Masing-masing jenis kesalahan ini mencoba untuk menyimpan informasi sebanyak mungkin selama mungkin untuk pencetakan cantik atau analisis lainnya. Lebih penting lagi, dengan tidak memiliki string saya bisa menguji bahwa menjalankan program yang diketik melalui pemeriksa tipe benar-benar menghasilkan kesalahan unifikasi! Setelah sesuatu adalah String
, itu hilang selamanya dan setiap informasi yang dikandungnya tidak jelas untuk kompiler / tes, jadi Either String
tidak bagus juga.
Akhirnya saya kemas tipe ini ExceptT
, trafo monad baru dari MTL. Ini pada dasarnya EitherT
dan dilengkapi dengan sejumlah fungsi yang bagus untuk melempar dan menangkap kesalahan dengan cara yang murni dan menyenangkan.
Akhirnya, perlu disebutkan bahwa Haskell memiliki mekanisme untuk mendukung penanganan pengecualian seperti bahasa lain, kecuali bahwa menangkap pengecualian tinggal IO
. Saya tahu beberapa orang suka menggunakan ini untuk IO
aplikasi berat di mana semuanya berpotensi gagal, tetapi sangat jarang sehingga mereka tidak suka memikirkannya. Apakah Anda menggunakan pengecualian tidak murni ini atau hanya ExceptT Error IO
benar-benar masalah selera. Secara pribadi saya memilih ExceptT
karena saya suka diingatkan akan kemungkinan gagal.
Sebagai ringkasan,
Maybe
- Saya bisa gagal dalam satu cara yang jelas
Either CustomType
- Saya bisa gagal, dan saya akan memberi tahu Anda apa yang terjadi
IO
+ pengecualian - Terkadang saya gagal. Periksa dokumen saya untuk melihat apa yang saya lemparkan ketika saya melakukannya
error
- Aku benci kamu juga pengguna
head
ataulast
sepertinya digunakanerror
, jadi saya bertanya-tanya apakah itu sebenarnya cara yang baik untuk melakukan sesuatu dan saya hanya melewatkan sesuatu. Ini menjawab pertanyaan itu. :)Saya tidak akan memisahkan 2 dan 3 berdasarkan berapa banyak cara sesuatu dapat gagal. Segera setelah Anda berpikir hanya ada satu cara yang mungkin, yang lain akan menunjukkan wajahnya. Metriknya seharusnya "apakah penelepon saya peduli mengapa sesuatu gagal? Bisakah mereka benar-benar melakukan sesuatu tentang hal itu?".
Di luar itu, tidak jelas bagi saya yang
Either String SomeType
dapat menghasilkan kondisi kesalahan. Saya akan membuat tipe data aljabar sederhana dengan kondisi dengan nama yang lebih deskriptif.Yang Anda gunakan tergantung pada sifat masalah yang Anda hadapi, dan idiom dari paket perangkat lunak yang Anda gunakan. Padahal saya cenderung menghindari # 1.
sumber
Either
untuk kesalahan adalah pola yang sangat terkenal di Haskell.Either
bukan bagian yang tidak jelas, penggunaanString
sebagai kesalahan daripada string yang sebenarnya.