MaybeT的这种特殊用途如何运作?

时间:2014-04-17 17:53:26

标签: haskell

我正在尝试了解monad变换器,我正在阅读haskell wiki上的this page

我困惑的代码如下:

isValid :: String -> Bool
isValid s = length s >= 5

getValidPassword :: MaybeT IO String
getValidPassword = do s <- lift getLine
                      guard (isValid s)
                      return s

askPassword :: MaybeT IO ()
askPassword = do lift $ putStrLn "Insert your new password:"
                 value <- getValidPassword
                 lift $ putStrLn "Storing in database..."

到目前为止一切顺利。我可以运行它,它的工作原理。但现在,将askPassword更改为:

askPassword :: MaybeT IO ()
askPassword = do lift $ putStrLn "Insert your new password:"
                 value <- msum $ repeat getValidPassword
                 lift $ putStrLn "Storing in database..."

如果不满足有效条件,这将反复等待我提供新的输入。我有点迷失它是怎么做到的。 repeat会永久重复此操作,而msum只是foldr mplus mzero,这意味着它会沿着列表行进,并将这些值“添加”在一起。

为什么这不会在我输入错误后立即返回(返回值为Nothing)。我想我没有看到决定何时停止以及何时继续的逻辑嵌入的位置。谢谢你的帮助。

2 个答案:

答案 0 :(得分:5)

嗯,msum的{​​{1}}应该在第一次成功尝试时停止,而不是第一次失败。

答案 1 :(得分:4)

这是有效的,因为MaybeT的mplus在其第二个参数中是非严格的:

mplus (return () :: MaybeT m ()) undefined

相当于

return ()

这是the definition of mplus for MaybeT的结果,如下所示:

instance (Monad m) => MonadPlus (MaybeT m) where
  mzero = MaybeT (return Nothing)
  mplus x y = MaybeT $ do v <- runMaybeT x
                          case v of
                            Nothing -> runMaybeT y
                            Just _  -> return v

如您所见,除非第一个参数y无法返回结果,否则不会执行mplus的第二个参数x

因此,从msum获得的无限动作列表中的repeat getPassword只会在第一个成功之前执行。