理解 MonadTransformer 示例

understanding MonadTransformer examples

我正在 https://en.wikibooks.org/wiki/Haskell/Monad_transformers

学习教程

我写了下面的一段代码。一个没有,另一个有 MonadTransformer 实例:

-- Simple Get Password functions. 
getPassphrase1 :: IO (Maybe String)
getPassphrase1 = do
  password <- getLine
  if isValid password
  then return $ Just password
  else return Nothing


askPassphrase1 :: IO ()
askPassphrase1 = do
  putStrLn "Enter password < 8 , alpha, number and punctuation:"
  p <- getPassphrase1
  case p of
    Nothing -> do          -- Q1. ### How to implement this with `MonadTrans` ?
      putStrLn "Invalid password. Enter again:"
      askPassphrase1
    Just password ->
      putStrLn $ "Your password is " ++ password

-- The validation test could be anything we want it to be.
isValid :: String -> Bool
isValid s = length s >= 8
            && any isAlpha s
            && any isNumber s
            && any isPunctuation s

另一个使用我自己写的 MonadT

getPassphrase2 :: MaybeT IO String
getPassphrase2 = do
  password <- lift getLine
  guard $ isValid password
  return password

askPassphrase2 :: MaybeT IO ()
askPassphrase2 = do
  lift $ putStrLn "Enter password < 8 , alpha, number and punctuation:"
  p <- getPassphrase2
  -- Q1. How to print "Invalid password" ?
  lift $ putStrLn $ "Your password is " ++ p

-- The validation test could be anything we want it to be.
isValid :: String -> Bool
isValid s = length s >= 8
            && any isAlpha s
            && any isNumber s
            && any isPunctuation s

main :: IO ()
main = do
  a <- runMaybeT askPassphrase2
  return ()

两者都有效。

但我无法理解如何在 MonadTrans 示例中添加 wrong password 支持。 ?

此外,main 方法是否可以..或者可以用更好的方式编写?

guard 不是您想要的 MaybeT 方法。要检查无效密码 and 能够在这种情况下提供您自己的处理,您只需使用原始版本的 getPassphaselift 它进入 MaybeT IO monad:

getPassphease2 = do result <- lift $ getPassphrase1
                    case result of
                        Nothing -> lift $ putStrLn "bad password"
                        Just pw -> lift $ putStrLn "your password is: " ++ pw

解释...

MaybeT IO monad 用于为 IO 操作提供失败的能力 让 monad 自动处理失败。如果任何步骤失败,控制将一直返回到 runMaybeTrunMaybeT returns Nothing。这就像抛出一个异常。

使用 MaybeT 的要点是您不必显式检查步骤是否失败 - 检查由 MaybeT monad 在调用每个步骤后执行。这意味着您可以假设前面的每个步骤都已成功编写代码 - 即,就好像您在 "happy path" 上一样。这也意味着如果上一步失败,您将无法执行其他操作。

使用 getPassphraseMaybeT 版本的一种可能性是:

main = do result <- runMaybeT askPassphrase2
          case result of
            Just _  -> return ()
            Nothing  -> putStrLn "Some failure happened... perhaps wrong password?"

问题是,如果 runMaybeT returns Nothing 这可能意味着 askPassphrase 中的任何步骤都失败了,而不仅仅是 guard

另一种使用 MaybeT 版本的 getPassphrase 的方法是 askPassphrase 运行 它与 runMaybeT:

askPassphrase2 = do result <- lift $ runMaybeT getPassphrase2
                    case result of
                      Nothing -> lift $ putStrLn "bad password"
                      Just pw -> lift $ putStrLn $ "Your password is " ++ pw

我们在这里使用 runMaybeT 作为 try-catch 块。