11

在以下 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

4 回答 4

13

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

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

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

于 2012-05-05T17:54:20.180 回答
2

在 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")

因为在 的情况下fail会引发IOError异常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 ++ "\""
于 2012-05-06T06:56:36.260 回答
2

我个人建议readMaysafe包中使用:

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

然后,您可以对“Maybe a”结果进行模式匹配,使用“maybe”,甚至使用“Maybe”单子来处理结果。

于 2012-05-05T19:32:13.853 回答
2

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

于 2012-05-05T08:17:36.070 回答