How to use maybe monad inside another monad?

happstack, haskell

Solution

Ok, with your generic example I could do something with `Control¸Monad.Transformers`. This allows you to create a stack of monads. You can check it out here: http://hackage.haskell.org/package/transformers-0.3.0.0/docs/Control-Monad-Trans-Maybe.html You can apply MaybeT to everything of type `IO (Maybe a)` and then do all the computations in the inner do block 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 `liftIO` and the `Alternative` type 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 using many lift operations is not very elegant.

Problem

I have this code (inside happstack, but could be just the 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 up as a type of `Maybe String` rather than just `String`. How can I evaluate the nested `do` block using the maybe monad? (I would also accept a different refactoring that removes the nested cases.) UPDATE: Below is a generic, though contrived, 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!" ```

Original source