如何在另一个monad中使用可能的monad



我有以下代码(在happstack中,但可能只是IO monad):

accountHandler conn = do
  sessionId <- optional $ readCookieValue "sessionId"
  case sessionId of
    Nothing -> seeOther ("/" :: String) $ toResponse ()
    Just s  -> do
      result <- loggedInUserId conn s
      case result of
        Just userId -> seeOther ("/account/" ++ unUserId userId) $ toResponse ()
        Nothing -> seeOther ("/" :: String) $ toResponse ()

我想删除嵌套的case语句,并编写如下内容:

accountHandler conn = do
  let action = do
                sessionId <- optional $ readCookieValue "sessionId"
                userId    <- loggedInUserId conn sessionId
                return $ seeOther ("/account/" ++ userId)
  maybe (seeOther ("/" :: String)) id action $ toResponse ()

但是userId最终是一种类型的CCD_ 1而不仅仅是CCD_。如何使用maybe monad评估嵌套的do块?(我也会接受一个不同的重构,删除嵌套的案例。)

更新:下面是同一问题的一般版本,尽管是人为的:

module Main where
getAnswer expected = do
  l <- getLine
  if l == expected
    then return $ Just l
    else return $ Nothing
main = do
  a <- getAnswer "a"
  case a of
    Nothing -> putStrLn "nope"
    Just x -> do
      b <- getAnswer x
      case b of
        Nothing -> putStrLn "nope"
        Just _ -> putStrLn "correct!"

好的,使用您的通用示例,我可以使用Control¸Monad.Transformers做一些事情。这允许您创建一个monad堆栈。你可以在这里查看:http://hackage.haskell.org/package/transformers-0.3.0.0/docs/Control-Monad-Trans-Maybe.html您可以将MaybeT应用于IO (Maybe a)类型的所有内容,然后在内部do块中进行所有计算,然后在末尾检查Nothing。

module Main where
import Control.Monad.Trans.Maybe

getAnswer expected = MaybeT $ do
       l <- getLine
       if l == expected
       then return $ Just l
       else return $ Nothing
main = do
    y <- runMaybeT $ do a <- getAnswer "a"
                        b <- getAnswer a
                        return b
    case y of Nothing  -> putStrLn "failure"
              (Just _) -> putStrLn "correct"

使用liftIOAlternative类型类的另一个版本:

module Main where
import Control.Monad.Trans.Maybe
import Control.Monad.IO.Class
import Control.Applicative

getAnswer expected = MaybeT $ do
  l <- getLine
  if l == expected
    then return $ Just l
    else return $ Nothing
main = do
    _ <- runMaybeT $ do a <- getAnswer "a"
                        b <- getAnswer a
                        liftIO $ putStrLn "correct" 
                   <|> do liftIO $ putStrLn "failure"
    return ()

但使用许多升降机操作并不是很优雅。

我想在MoFu的回答中补充一点,一旦你有了MaybeT IO,你就可以使用它的MonadPlus实例的全部功能。例如,如果需要检查某个条件是否成立,请使用Maybe String0或mfilter。所以你可以写:

import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans
import Control.Monad.Trans.Maybe
getAnswer :: (MonadPlus m, MonadIO m) => String -> m String
getAnswer expected = mfilter (== expected) $ liftIO getLine

它的类型非常通用,适用于MonadPlusMonadIO的任何monad。如果您决定稍后修改monad堆栈,这将非常方便。但我们也可以使用更具体的类型(MonadIO m) => String -> MaybeT m String

为了从内部计算中提取MaybeT IO值,我建议为MaybeT编写fromMaybe的变体:

fromMaybeT :: (Monad m) => m a -> MaybeT m a -> m a
fromMaybeT onFail = maybe onFail return <=< runMaybeT

用CCD_ 18提取结果。如果是Just,只需返回它,否则运行String0操作。

结合在一起,我们得到:

main = fromMaybeT (putStrLn "nope") $ do
  a <- getAnswer "a"
  b <- getAnswer a
  liftIO $ putStrLn "correct!"

相关内容

  • 没有找到相关文章

最新更新