haskell wikibook monadplus潜在的错误

时间:2016-09-04 00:14:12

标签: haskell

正在阅读haskell wiki书的monadplus章节:https://en.wikibooks.org/wiki/Haskell/MonadPlus

digit :: Int -> String -> Maybe Int
digit i s | i > 9 || i < 0 = Nothing
          | otherwise      = do
  let (c:_) = s
  if [c] == show i then Just i else Nothing

“do-block确保任何失败的模式匹配都会导致返回Nothing。”

然而,当我尝试digit 1 ""时,它会产生无法反驳的运行时错误,而不是“Nothing”。

我可以使用(c:_) <- return s解决这个问题。如果有更多经验丰富的哈斯克尔能够证实/澄清这一点,那就太棒了。

1 个答案:

答案 0 :(得分:2)

wikibooks中的代码不考虑输入字符串为空时的情况。执行行let (c:_) = s并且s为空时,将导致模式匹配失败,并抛出异常。您的建议(c:_) <- return s实际上与使用的建议完全相似,只有一个区别;当monadic绑定中的模式匹配(即<-)失败时,将调用monad的fail方法。现在,在Maybe monad中,fail defined始终返回Nothing,因此会导致整个do块返回Nothing 。我不喜欢使用你的建议的一件事是,我个人不认为使用fail是最优雅的解决方案,我宁愿在这种情况下使用case表达式:

digit :: Int -> String -> Maybe Int
digit i s | i > 9 || i < 0 = Nothing
          | otherwise      =
    case s of
        c:_ | [c] == show i -> Just i
        _ -> Nothing

事实上,正如您所看到的,我们根本不需要使用do块。

最后,这是上述代码的更紧凑版本:

digit :: Int -> String -> Maybe Int
digit i s | 0 <= i, i <= 9, c:_ <- s, [c] == show i = Just i
          | otherwise = Nothing