我有以下代码(在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"
使用liftIO
和Alternative
类型类的另一个版本:
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 String
0或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
它的类型非常通用,适用于MonadPlus
和MonadIO
的任何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
,只需返回它,否则运行String
0操作。
结合在一起,我们得到:
main = fromMaybeT (putStrLn "nope") $ do
a <- getAnswer "a"
b <- getAnswer a
liftIO $ putStrLn "correct!"