How to use a monad inside another monad?

I have this code (inside happstack, but can only be an 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 () 

I want to remove the nested case statements and write something like:

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

... but userId ends as Maybe String , not just String . How can I evaluate a nested do block using perhaps a monad? (I also accept another refactoring that removes nested cases.)

UPDATE: The following is a general, albeit far-fetched, version of the same problem:

 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!" 
+7
haskell happstack
source share
2 answers

Well, with your general example, I could do something with Control¸Monad.Transformers . This allows you to create a stack of monads. You can check it here: http://hackage.haskell.org/package/transformers-0.3.0.0/docs/Control-Monad-Trans-Maybe.html You can apply MaybeT to the whole type of IO (Maybe a) , and then do all the calculations in the do block inside and then check for Nothing at the end.

 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" 

Another version using the liftIO and Alternative class:

 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 () 

But the use of many lifting operations is not very elegant.

+5
source share

I would like to add to MoFu's answer that if you have MaybeT IO , you can use the full power of your MonadPlus instance. For example, if you need to verify that a condition is met, use guard or mfilter . Therefore, you can write:

 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 

This type is very common, it works for any monad that is MonadPlus and MonadIO . This is convenient if you decide to change your monad stack later. But we could also use a more specific type (MonadIO m) => String -> MaybeT m String .

To extract the MaybeT IO value from your internal calculation, I would suggest writing a variant fromMaybe for MaybeT :

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

It retrieves the result using runMaybeT . If it is Just , just return it, otherwise do the onFail action.

Together we get:

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

All Articles