Các ngoại lệ trong Haskell hoạt động như thế nào?


87

Trong GHCi:

Prelude> error (error "")
*** Exception: 
Prelude> (error . error) ""
*** Exception: *** Exception: 

Tại sao cái đầu tiên không phải là một ngoại lệ lồng nhau?


9
Đây là một sự chuyển đổi mà GHC được phép thực hiện: "Tôi là Trình biên dịch tự vận hành, và tất cả _ | _ đều giống tôi". Bạn đang yêu cầu các chi tiết triển khai làm cho hai dòng biên dịch khác nhau?
shachaf

3
errorlà đặc biệt và không thực sự là một cơ chế ngoại lệ. Đối với các ngoại lệ thực, có thể bắt được xem Errorđơn nguyên.
Cat Plus Plus

1
Lưu ý, ví dụ, (\f g x -> f (g x)) error error ""hoạt động khác với (.) error error "", mặc dù chức năng đó tương đương với (.). Có lẽ nó liên quan đến các cờ tối ưu hóa mà Prelude đã được biên dịch.
shachaf

5
Ngoài ra iterate error "" !! nvà tuyệt vời fix error.
Vitus

9
Tôi luôn giả vờ như vậy error = errorvà lập trình cho phù hợp.
Gabriel Gonzalez

Câu trả lời:


102

Câu trả lời là đây là ngữ nghĩa (hơi ngạc nhiên) của các ngoại lệ không chính xác

Khi mã thuần túy có thể được hiển thị để đánh giá một tập hợp các giá trị đặc biệt (tức là giá trị của errorhoặc undefinedvà rõ ràng không phải là loại ngoại lệ được tạo ra trong IO ), thì ngôn ngữ cho phép trả về bất kỳ giá trị nào của tập hợp đó. Các giá trị ngoại lệ trong Haskell giống NaNtrong mã dấu phẩy động hơn là các ngoại lệ dựa trên luồng điều khiển trong các ngôn ngữ mệnh lệnh.

Một gotcha không thường xuyên cho các Haskellers nâng cao thậm chí là một trường hợp như:

 case x of
   1 -> error "One"
   _ -> error "Not one"

Vì mã đánh giá một tập hợp các trường hợp ngoại lệ, nên GHC có quyền chọn một trường hợp ngoại lệ. Khi bật tối ưu hóa, bạn có thể thấy điều này luôn được đánh giá là "Không phải một".

Tại sao chúng ta làm việc này? Bởi vì nếu không, chúng tôi sẽ quá hạn chế thứ tự đánh giá của ngôn ngữ, ví dụ: chúng tôi sẽ phải sửa một kết quả xác định cho:

 f (error "a") (error "b")

chẳng hạn, yêu cầu nó được đánh giá từ trái sang phải nếu có giá trị lỗi. Rất không Haskelly!

Vì chúng tôi không muốn làm tê liệt các tối ưu hóa có thể được thực hiện trên mã của chúng tôi chỉ để hỗ trợ error, giải pháp là chỉ định rằng kết quả là một lựa chọn không xác định từ tập hợp các giá trị đặc biệt: ngoại lệ không chính xác! Theo một cách nào đó, tất cả các ngoại lệ đều được trả về và một ngoại lệ được chọn.

Thông thường, bạn không quan tâm - một ngoại lệ là một ngoại lệ - trừ khi bạn quan tâm đến chuỗi bên trong ngoại lệ, trong trường hợp đó, việc sử dụng errorđể gỡ lỗi rất khó hiểu.


Tài liệu tham khảo: Ngữ nghĩa cho các trường hợp ngoại lệ không chính xác , Simon Peyton Jones, Alastair Reid, Tony Hoare, Simon Marlow, Fergus Henderson. Thiết kế và triển khai ngôn ngữ lập trình Proc (PLDI'99), Atlanta. ( PDF )


2
Tôi hiểu GHC chọn một trong bất kỳ trường hợp ngoại lệ nào có thể gặp phải. Nhưng trong ví dụ "trường hợp" của bạn, không thể gặp ngoại lệ "Không phải là một" cho đầu vào là 1, vì vậy tôi vẫn phân loại đó là lỗi.
Peaker

8
Mã chết @Peaker elim - trình tối ưu hóa không cần nhìn vào x để biết rằng lỗi là kết quả, tất cả các nhánh đều tạo ra giá trị "giống nhau", vì vậy nó có thể bỏ qua hoàn toàn giá trị đầu vào. Không phải là một lỗi, trong các trường hợp ngoại lệ không chính xác!
Don Stewart

1
@lpsmith: Tôi nghĩ rằng tất cả các loại ngoại lệ đều không chính xác (khi sử dụng throw) và bạn có thể xác định ném một ngoại lệ throwIO.
FunctorSalad

4
@Peaker Tôi nghĩ bạn đúng. Tôi không nghĩ ghc nên tối ưu hóa biểu thức này nếu họ muốn chơi theo các quy tắc được nêu trong bài báo ngoại lệ không chính xác.
augustss

3
Có gì giấy ngoại lệ không chính xác không dường như nói là nếu trường hợp scrutinee là một giá trị lỗi, sau đó giá trị lỗi từ các chi nhánh có thể được trả lại. Vì vậy, case error "banana" of (x:xs) -> error "bonobo"có thể cung cấp cho bạn * Exception: bonobo.
Ben Millwood
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.