Được rồi, quy tắc xử lý lỗi đầu tiên trong Haskell: Không bao giờ sử dụngerror
.
Nó chỉ là khủng khiếp về mọi mặt. Nó tồn tại hoàn toàn như một hành động của lịch sử và thực tế là Prelude sử dụng nó thật kinh khủng. Đừng sử dụng nó.
Thời gian có thể tưởng tượng duy nhất mà bạn có thể sử dụng là khi một thứ gì đó quá khủng khiếp đến nỗi có gì đó không ổn với chính kết cấu thực tế, do đó đưa ra kết quả của chương trình của bạn.
Bây giờ câu hỏi trở thành Maybe
vs Either
. Maybe
là rất phù hợp với một cái gì đó như head
, có thể hoặc không thể trả về một giá trị nhưng chỉ có một lý do có thể để thất bại. Nothing
đang nói điều gì đó như "nó đã phá vỡ, và bạn đã biết tại sao". Một số người sẽ nói nó chỉ ra một phần chức năng.
Hình thức xử lý lỗi mạnh mẽ nhất là Either
+ ADT lỗi.
Ví dụ, trong một trong những trình biên dịch sở thích của tôi, tôi có một cái gì đó như
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
Bây giờ tôi xác định một loạt các loại lỗi, lồng chúng để tôi có một lỗi cấp cao nhất. Đây có thể là một lỗi từ bất kỳ giai đoạn biên dịch hoặc một ImpossibleError
, điều này biểu thị một lỗi biên dịch.
Mỗi loại lỗi này cố gắng giữ càng nhiều thông tin càng lâu càng tốt để in đẹp hoặc phân tích khác. Quan trọng hơn, bằng cách không có một chuỗi, tôi có thể kiểm tra rằng việc chạy một chương trình bị lỗi thông qua trình kiểm tra loại thực sự tạo ra một lỗi thống nhất! Một khi một cái gì đó là một String
, nó sẽ biến mất mãi mãi và bất kỳ thông tin nào chứa trong đó đều mờ đục đối với trình biên dịch / kiểm tra, vì vậy cũng Either String
không phải là tuyệt vời.
Cuối cùng tôi đóng gói loại này vào ExceptT
, một máy biến áp đơn nguyên mới từ MTL. Điều này về cơ bản EitherT
và đi kèm với một loạt các chức năng tốt đẹp để ném và bắt lỗi theo cách thuần túy, dễ chịu.
Cuối cùng, điều đáng nói là Haskell có các cơ chế hỗ trợ xử lý các trường hợp ngoại lệ như các ngôn ngữ khác, ngoại trừ việc bắt một ngoại lệ tồn tại IO
. Tôi biết một số người thích sử dụng những IO
ứng dụng này cho các ứng dụng nặng, nơi mọi thứ có thể có khả năng thất bại, nhưng không thường xuyên đến mức họ không muốn nghĩ về nó. Cho dù bạn sử dụng những ngoại lệ không tinh khiết này hay chỉ ExceptT Error IO
thực sự là một vấn đề của hương vị. Cá nhân tôi chọn ExceptT
vì tôi thích được nhắc nhở về cơ hội thất bại.
Như một bản tóm tắt,
Maybe
- Tôi có thể thất bại theo một cách rõ ràng
Either CustomType
- Tôi có thể thất bại, và tôi sẽ kể cho bạn nghe chuyện gì đã xảy ra
IO
+ ngoại lệ - Đôi khi tôi thất bại. Kiểm tra tài liệu của tôi để xem những gì tôi ném khi tôi làm
error
- Tôi ghét bạn quá người dùng
head
hoặclast
dường như sử dụngerror
, vì vậy tôi đã tự hỏi liệu đó có thực sự là một cách tốt để làm mọi thứ và tôi chỉ thiếu một cái gì đó. Câu trả lời này. :)