16

Tôi ngạc nhiên vì tôi không thể tìm thấy câu trả lời cho điều này ở bất cứ đâu.Cách bắt (và bỏ qua) cuộc gọi đến chức năng lỗi?

Tôi đang viết một roguelike và tôi đang sử dụng thư viện ncurses từ hackage, một trình bao bọc khá tốt xung quanh thư viện ncurses. Bây giờ ncurses có quirk này, nơi nếu bạn cố gắng để viết các ký tự dưới cùng bên phải, nó làm như vậy, sau đó nó cố gắng di chuyển con trỏ đến ký tự tiếp theo, sau đó nó không thành công vì không có nơi nào để di chuyển nó đến. Nó trả về một giá trị lỗi mà bạn chỉ có thể bỏ qua.

Vấn đề của tôi là bộ gõ thư viện haskell ncurses dutifully kiểm tra cho bất kỳ lỗi trên tất cả các cuộc gọi, và khi có một, ông gọi: lỗi "drawText: vv vv".

Trong các ngôn ngữ khác, như c hoặc python, để giải quyết vấn đề này, bạn buộc phải bỏ qua lỗi hoặc bắt và bỏ qua ngoại lệ, nhưng đối với cuộc sống của tôi, tôi không thể tìm ra cách để làm điều đó trong haskell. Chức năng lỗi có thể khôi phục được không?

Tôi sẽ sửa đổi thư viện cục bộ để không kiểm tra lỗi trên chức năng đó nếu tôi có, nhưng tôi ghét làm điều đó. Tôi cũng mở cửa cho bất kỳ giải pháp nào cho phép tôi vẽ nhân vật cuối cùng mà không di chuyển con trỏ, nhưng tôi không nghĩ điều đó là có thể.

+0

Hoogle vì "bắt" [1]. Liên kết thứ hai xuống. [1] http://haskell.org/hoogle/?hoogle=catch –

+0

Rất tiếc, lỗi được đề cập không có trong đơn nguyên IO. Vâng, nó bắt đầu trong IO, sau đó bạn chạy runCurses, đó là các đơn nguyên Curses, sau đó updateWindow, là bản cập nhật. Vì vậy, tôi không nghĩ rằng câu trả lời của paul sẽ làm việc. Nhưng có vẻ như luqui có tiềm năng và tôi sẽ thử nó khi tôi về nhà. –

+0

tìm kiếm trên ncurses Tôi nhận được cảm giác nó sẽ không hoạt động. 'drawText' không gọi' error', nó đại biểu thẳng đến hàm C. Và nó trả về trong đơn 'Update', đó là' ReaderT Window IO a' = 'Cửa sổ -> IO a', vì vậy' unsafeCleanup' sẽ chỉ hoạt động nếu nó bị lỗi khi tạo * chức năng đó *, không phải khi chạy hành động (không). Tôi nghĩ rằng các tùy chọn của bạn là: bắt lỗi trong IO ở cấp cao nhất, hoặc mở nguồn curses để cho phép bạn tiêm một hàm 'catch' cục bộ hơn. (Nó có thể được thực hiện dễ dàng, chỉ cần phá vỡ đóng gói) – luqui

Trả lời

12

error được coi là có thể quan sát dưới dạng vòng lặp vô hạn. Bạn chỉ có thể bắt được error trong số IO, giống như nói "vâng bạn có thể nếu bạn biết ma thuật". Nhưng từ phần thực sự tốt đẹp của Haskell, mã thuần túy, nó không thể khôi phục được, và do đó được khuyên là không phải để sử dụng trong mã của bạn, chỉ nhiều như bạn sử dụng vòng lặp vô hạn làm mã lỗi.

ncurses đang thô lỗ và khiến bạn thực hiện phép thuật để sửa nó. Tôi muốn nói rằng unsafePerformIO sẽ được bảo hành để làm sạch nó. Ngoài ra, điều này phần lớn giống như câu trả lời của Phao-lô.

import qualified Control.Exception as Exc 

{-# NOINLINE unsafeCleanup #-} 
unsafeCleanup :: a -> Maybe a 
unsafeCleanup x = unsafePerformIO $ Exc.catch (x `seq` return (Just x)) handler 
    where 
    handler exc = return Nothing `const` (exc :: Exc.ErrorCall) 

Sau đó quấn unsafeCleanup xung quanh bất kỳ giá trị mà sẽ đánh giá một lỗi để biến nó thành một Maybe.

+0

btw, 'Exc.catch (Exc.evaluate (Chỉ $! x)) handler' sẽ hơi sạch hơn imho – hvr

+1

FWIW này hiện được triển khai trong thư viện [spoon] (http://hackage.haskell.org/package/spoon). – luqui

+0

Có thể muỗng cũng làm 'a -> Hoặc là chuỗi a' thay vì' a -> Có thể a' để tôi có thể thấy thông báo lỗi đã bị bắt? Hoặc sẽ được chống chỉ định? –

15

Bạn có thể thực hiện việc này bằng cách sử dụng catch từ Control.Exception. Tuy nhiên, lưu ý rằng bạn cần phải ở trong đơn vị IO để thực hiện việc này.

import qualified Control.Exception as Exc 

divide :: Float -> Float -> Float 
divide x 0 = error "Division by 0." 
divide x y = x/y 

main :: IO() 
main = Exc.catch (print $ divide 5 0) handler 
    where 
     handler :: Exc.ErrorCall -> IO() 
     handler _ = putStrLn $ "You divided by 0!" 
+0

Tôi nghĩ rằng '$' char trong putStrLn có thể được gỡ bỏ xD. Nhưng đây phải là câu trả lời được chấp nhận, vì nó sạch hơn – dani24

Các vấn đề liên quan