Tôi hiện đang học Haskell. Một trong những động lực tại sao tôi chọn ngôn ngữ này là viết phần mềm với mức độ mạnh mẽ rất cao, tức là các hàm được xác định đầy đủ, xác định toán học và không bao giờ bị lỗi hoặc tạo ra lỗi. Tôi không có nghĩa là thất bại gây ra bởi các vị từ của hệ thống ("hệ thống ra khỏi bộ nhớ", "máy tính trên lửa" vv), những người không phải là thú vị và có thể chỉ đơn giản là sụp đổ toàn bộ quá trình. Tôi cũng không có nghĩa là hành vi sai trái do khai báo không hợp lệ (pi = 4
). Thay vào đó tôi tham khảo các lỗi gây ra bởi các trạng thái sai lầm mà tôi muốn loại bỏ bằng cách làm cho các trạng thái đó không thể đại diện và không thể compilable (trong một số hàm) thông qua gõ tĩnh nghiêm ngặt. Trong tâm trí của tôi, tôi gọi những hàm này là "thuần khiết" và nhận ra rằng hệ thống kiểu mạnh mẽ sẽ cho phép tôi thực hiện điều này. Tuy nhiên, Haskell không định nghĩa "thuần túy" theo cách này và cho phép các chương trình gặp sự cố thông qua error
trong mọi ngữ cảnh.Mạnh mẽ haskell mà không có lỗi
Why is catching an exception non-pure, but throwing an exception is pure?
Điều này hoàn toàn có thể chấp nhận được và không lạ chút nào. Điều đáng thất vọng tuy nhiên là Haskell không xuất hiện để cung cấp một số chức năng để cấm định nghĩa chức năng có thể dẫn đến các chi nhánh bằng cách sử dụng error
.
Dưới đây là một ví dụ contrived tại sao tôi tìm thấy điều này đáng thất vọng:
module Main where
import Data.Maybe
data Fruit = Apple | Banana | Orange Int | Peach
deriving(Show)
readFruit :: String -> Maybe Fruit
readFruit x =
case x of
"apple" -> Just Apple
"banana" -> Just Banana
"orange" -> Just (Orange 4)
"peach" -> Just Peach
_ -> Nothing
showFruit :: Fruit -> String
showFruit Apple = "An apple"
showFruit Banana = "A Banana"
showFruit (Orange x) = show x ++ " oranges"
printFruit :: Maybe Fruit -> String
printFruit x = showFruit $ fromJust x
main :: IO()
main = do
line <- getLine
let fruit = readFruit line
putStrLn $ printFruit fruit
main
Hãy nói rằng tôi hoang tưởng rằng các chức năng tinh khiết readFruit
và printFruit
thực sự không thất bại do tiểu bang unhanded. Bạn có thể tưởng tượng rằng mã là để tung ra một tên lửa đầy đủ các phi hành gia mà trong một thói quen hoàn toàn quan trọng cần phải tuần tự hóa và unserialize các giá trị trái cây.
Nguy hiểm đầu tiên là tự nhiên mà chúng tôi đã phạm sai lầm trong kết hợp mẫu của chúng tôi vì điều này cho chúng ta những trạng thái sai lầm đáng sợ không thể xử lý được. Rất may Haskell cung cấp được xây dựng theo những cách để ngăn chặn những người, chúng tôi chỉ đơn giản là biên dịch chương trình của chúng tôi với -Wall
trong đó bao gồm -fwarn-incomplete-patterns
và AHA:
src/Main.hs:17:1: Warning:
Pattern match(es) are non-exhaustive
In an equation for ‘showFruit’: Patterns not matched: Peach
Chúng tôi quên serialize trái cây Peach và showFruit
sẽ ném một lỗi. Đó là một sửa chữa dễ dàng, chúng tôi chỉ cần thêm:
showFruit Peach = "A peach"
Chương trình hiện đang biên dịch mà không có cảnh báo, nguy hiểm bị đảo ngược! Chúng tôi khởi động tên lửa nhưng đột nhiên chương trình bị treo với:
Maybe.fromJust: Nothing
Các tên lửa được cam chịu và truy cập các đại dương gây ra bởi dòng bị lỗi sau:
printFruit x = showFruit $ fromJust x
Về cơ bản fromJust
có một chi nhánh nơi mà nó đặt ra một Error
vì vậy chúng tôi thậm chí không muốn chương trình biên dịch nếu chúng tôi cố gắng sử dụng nó từ printFruit
hoàn toàn phải là "siêu" thuần túy. Chúng ta có thể cố định ví dụ bằng cách thay thế phù hợp với:
printFruit x = maybe "Unknown fruit!" (\y -> showFruit y) x
tôi thấy nó lạ mà Haskell quyết định để thực hiện gõ nghiêm ngặt và đầy đủ phát hiện mô hình, tất cả trong việc theo đuổi nobel ngăn ngừa trạng thái không hợp lệ từ việc biểu diễn nhưng thác chỉ ở phía trước của dòng kết thúc bằng cách không cho các lập trình viên một cách để phát hiện các chi nhánh đến error
khi chúng không được phép.Theo một nghĩa nào đó, điều này làm cho Haskell kém mạnh mẽ hơn khi Java buộc bạn phải khai báo các ngoại lệ mà các chức năng của bạn được phép nâng lên.
Cách nhỏ nhất để thực hiện điều này sẽ đơn giản là không xác định error
theo cách nào đó, cục bộ cho hàm và hàm bất kỳ được sử dụng bởi phương trình của nó. Tuy nhiên điều này dường như không thể.
The wiki page about errors vs exceptions đề cập đến một tiện ích mở rộng được gọi là "Kiểm tra tĩnh mở rộng" cho mục đích này thông qua hợp đồng nhưng nó chỉ dẫn đến một liên kết bị hỏng.
Về cơ bản, nó tóm tắt: Làm cách nào để chương trình ở trên không biên dịch được vì nó sử dụng fromJust
? Tất cả các ý tưởng, đề xuất và giải pháp đều được chào đón.
(1) Tôi tin rằng [đây là những giấy tờ bạn đang tìm kiếm] (http://research.microsoft.com/~simonpj/papers/verify/index.htm). (2) Lưu ý rằng 'printFruit' là không cần thiết. Nếu một người dùng muốn hiển thị một 'Có lẽ trái cây ', cô ấy có thể sử dụng' fmap showFruit' và sau đó unwrap 'Maybe' một cách hợp lý. Điều đó tất nhiên không trả lời câu hỏi của bạn (sẽ tốt nếu giữ người dùng tránh xa 'fromJust'!), Do đó hãy tiếp tục :) – duplode
Tôi muốn Haskell có một bộ kiểm tra tổng thể. Tôi thậm chí sẽ hài lòng với một kiểm tra không có ngoại lệ kiểm tra không đầy đủ và sử dụng các chức năng một phần, nhưng không kiểm tra cho các vòng vô hạn. Tôi cũng muốn nâng cao các loại cảnh báo này thành lỗi, để tôi không thể dễ dàng bỏ qua chúng. Điều này có thể sẽ không đòi hỏi một lượng lớn nỗ lực để thực hiện trong GHC. – chi
Bạn đã xem Idris chưa? Nó có thể là ngôn ngữ bạn muốn. – user3237465