如何在Haskell中捕获读取异常?

时间:2012-05-05 07:58:25

标签: haskell try-catch

在下面的Haskell代码中:

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

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

如果我这样做:

guiString2Cmd "CmdOther"

一切正常。但是,如果我这样做:

guiString2Cmd "some wrong string"

代码崩溃而不是评估为CmdExit。

如何让代码处理异常而不是崩溃?

4 个答案:

答案 0 :(得分:13)

使用reads函数(总计),并将失败案例包装为Maybe,如下所示:

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

maybeRead是一种非常通用的安全解析方式。

答案 1 :(得分:2)

解决方案是简单地使用读取

答案 2 :(得分:2)

我个人建议使用safe包中的readMay

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

然后你可以在'Maybe a'结果上进行模式匹配,使用'maybe',或者甚至使用'Maybe'monad来处理结果。

答案 3 :(得分:1)

在monad中存在一种阅读的习语:

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

IO monad不安全:

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

因为failIOError的情况下会抛出IO例外,但是可以处理:

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

Maybe monad:

的情况下是安全的
> readM "CmdOther" :: Maybe Cmd
Just CmdOther
> readM "Cmd?Other" :: Maybe Cmd
Nothing

因为在这种情况下failconst Nothing

无论如何,如果您想要一个带有签名guiString2Cmd的总函数String -> Cmd,您可以像readM一样编写它:

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

然后:

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

稍微更通用的方法。

适用于*种:

class Failable0 t where
  fail0 :: t

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

然后:

instance Failable0 Cmd where
  fail0 = CmdExit

适用于* -> *种:

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 ++ "\""