Handling multiple IO operations that return optional values The following Haskell code reads a Pandoc document from the DB and then serves it to the user after making a transformation, after the first run it is stored in memory via IORef State, inside a [(String, Pandoc)] so the document can be quickly looked up again, without hitting the DB or doing the transformation. I don't feel like this code reflects that this is what is happening that well, there is a lot of handling Maybe's that I feel clouds the actual steps that make up this action.

module Api.ReadDocument (Route, action) where

import Control.Monad.Reader
import Data.Aeson.Types
import Data.Int
import Data.IORef
import Data.List
import Database.Persist.Postgresql (ConnectionPool, fromSqlKey, runSqlPool)
import Servant
import Servant.Auth.Server
import Text.Pandoc (Pandoc, def, readHtml, writeJSON)
--
import Config
import Queries
import State
import Types.DB
import Types.Pandoc

type Route = "read" :&gt; Capture "id" Int64 :&gt; Get '[JSON] Pandoc

action :: IORef State -&gt; ConnectionPool -&gt; AuthResult Account -&gt; Int64 -&gt; Handler Pandoc
action state pool (Authenticated _) id = viaCache state pool id
action _ _ _ = throwAll err401

viaCache :: IORef State -&gt; ConnectionPool -&gt; Int64 -&gt; Handler Pandoc
viaCache state pool id = do
  let key = show id
  state' &lt;- liftIO $ readIORef state
  maybe (retrieve state pool id) (return . snd) $ find ((== key) . fst) (cached state')

retrieve :: IORef State -&gt; ConnectionPool -&gt; Int64 -&gt; Handler Pandoc
retrieve state pool id = do
  doc &lt;- runSqlPool (Queries.getDocumentFromId id) pool
  maybe (throwError err404) (loadDocument state id) doc

loadDocument :: IORef State -&gt; Int64 -&gt; Document -&gt; Handler Pandoc
loadDocument state id doc = do
  let key = show id
      pd = transform . documentBody $ doc
  _ &lt;- liftIO $ updateState (CacheFile (key, pd)) state
  return pd


Previously this was one function, which contained nested case statements like so (pseudo code):

action state pool (Authenticated _) id = do
  state' &lt;- fromState state              -- get the state
  case find id state of                  -- find the data
    Just (_, doc) -&gt;
      return doc                         -- return cached document
    Nothing -&gt; do
      doc &lt;- getDocumentFromId id pool   -- get document from db
      case doc of
        Just d -&gt; do
          let doc = performOp $ d        -- perform computation on doc
          _ &lt;- saveToState state doc     -- save the document to in memory cache
          return doc                     -- serve document
        Nothing -&gt;                       -- document not actually in DB
          throwError err404


This is obviously much more explicit but I didn't like the nested case statements, and obviously there is a bit more to the code than what I have typed out here.
Here's how your pseudocode can be rewritten with bounded indentation using MaybeT.

action state pool (Authenticated _) id = cached &lt;|&gt; retrieve &lt;|!&gt; throwError err404 where
  cached = snd &lt;$&gt; MaybeT (find id &lt;$&gt; fromState state)
  retrieve = do 
    doc &lt;- performOp &lt;$&gt; MaybeT (getDocumentFromId id pool)
    _ &lt;- lift $ saveToState state doc
    return doc

infix 2 &lt;|!&gt;
(&lt;|!&gt;) :: Monad m =&gt; MaybeT m a -&gt; m a -&gt; m a
mx &lt;|!&gt; y = runMaybeT mx &gt;&gt;= maybe y pure