2012-05-05 39 views
9

Trong mã Haskell sau:Làm thế nào để bắt ngoại lệ đọc trong Haskell?

data Cmd = 
    CmdExit | 
    CmdOther 
    deriving (Read, Show) 

guiString2Cmd s = 
    (return (read s :: Cmd)) `catch` \(e :: SomeException) -> return CmdExit 

Nếu tôi làm:

guiString2Cmd "CmdOther" 

nó tất cả hoạt động tốt. Tuy nhiên, nếu tôi làm:

guiString2Cmd "some wrong string" 

sự cố mã thay vì đánh giá để CmdExit.

Tôi làm cách nào để mã xử lý ngoại lệ thay vì bị lỗi?

+0

Sử dụng nội dung đã có thể là một ý tưởng tốt hơn thực sự ... – mbrodersen

+1

Vâng, là tốt nhất cách để xử lý một ngoại lệ thuần túy là không tạo ra nó ngay từ đầu. – dave4420

+1

Đối với hồ sơ, ngoại lệ (bao gồm cả những người được ném bởi 'đọc') chỉ có thể bị bắt trong đơn IO, sử dụng các hàm được cung cấp trong' Control.Exception'. Tôi sẽ không đi vào chi tiết hơn bởi vì 'lần đọc' là một giải pháp tốt hơn. –

Trả lời

13

Sử dụng reads chức năng, đó là tổng, và quấn trường hợp thất bại như một Maybe, như vậy:

maybeRead :: Read a => String -> Maybe a 
maybeRead s = case reads s of 
    [(x, "")] -> Just x 
    _   -> Nothing 

maybeRead là khá một cách linh hoạt để làm phân tích an toàn.

+4

Chức năng này có sẵn dưới dạng ['Text.Read.readMaybe'] (http://www.haskell.org/ghc/docs/latest/html/libraries/base/Text-Read.html#v:readMaybe) trong gần đây phiên bản 'base'. – hammar

2

Giải pháp là chỉ cần sử dụng đọc thay thế.

2

Cá nhân tôi sẽ khuyên bạn sử dụng readMay từ gói safe:

readMay :: Read a => String -> Maybe a 

Sau đó, bạn có thể mô hình trận đấu trên 'Có lẽ một' kết quả, sử dụng 'có thể', hoặc thậm chí sử dụng 'Có thể' đơn nguyên để xử lý kết quả.

1

Có tồn tại một thành ngữ đọc bên trong một đơn nguyên:

readM :: (Monad m, Read a) => String -> m a 
readM s | [x] <- [x | (x, "") <- reads s] = return x 
     -- or @[x] <- [x | (x, _) <- reads s] = return [email protected] 
     -- to allow the garbage at the end of parsed string 
     | otherwise = fail $ "Failed to parse: \"" ++ s ++ "\"" 

đó là không an toàn cho IO đơn nguyên:

> readM "CmdOther" :: IO Cmd 
CmdOther 
> readM "Cmd?Other" :: IO Cmd 
*** Exception: user error (Failed to parse: "Cmd?Other") 

fail ném một ngoại lệ IOError trong trường hợp IO, trong đó, tuy nhiên , có thể được xử lý:

*Main> (readM "Cmd?Other" :: IO Cmd) `catch` const (return CmdOther) 
CmdOther 

Và an toàn trong trường hợp Maybe đơn nguyên:

> readM "CmdOther" :: Maybe Cmd 
Just CmdOther 
> readM "Cmd?Other" :: Maybe Cmd 
Nothing 

failconst Nothing trong trường hợp này.

Dù sao, nếu bạn muốn có một tổng chức năng guiString2Cmd với một chữ ký String -> Cmd bạn có thể viết nó giống như readM:

guiString2Cmd :: String -> Cmd 
guiString2Cmd s | [x] <- [x | (x, "") <- reads s] = x 
       | otherwise = CmdExit 

và sau đó:

> guiString2Cmd "CmdOther" 
CmdOther 
> guiString2Cmd "Cmd?Other" 
CmdExit 

cách tiếp cận Hơi chung chung hơn.

Đối * loại:

class Failable0 t where 
    fail0 :: t 

readG0 :: (Failable0 t, Read t) => String -> t 
readG0 s | [x] <- [x | (x, "") <- reads s] = x 
     | otherwise = fail0 

thì:

instance Failable0 Cmd where 
    fail0 = CmdExit 

Đối * -> * loại:

class Failable f where 
    fail :: String -> f a 

class Functor f => Pointed f where 
    pure :: a -> f a 

readG :: (Failable f, Pointed f, Read a) => String -> f a 
readG s | [x] <- [x | (x, "") <- reads s] = pure x 
     | otherwise = fail $ "Failed to parse: \"" ++ s ++ "\"" 
+0

'fail' không phải là' error' trong trường hợp 'IO', nó ném một ngoại lệ' IOError'. So sánh 'thất bại 'ồ không!" \ 'catch \' (\ e -> in (e :: IOError)) 'với' lỗi "oh không!" \ 'catch \' (\ e -> in (e :: IOError)) ' –

+0

@benmachine đúng,' 'thất bại"? " 'catch' const (return"! ")' '=>' "!" 'và' 'lỗi"? " 'catch' const (return"! ")' '=>' *** Ngoại lệ:? '. Cảm ơn bạn đã làm rõ. – JJJ

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