Problem

```
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
data E a
= E1
| E2
| E3 a
data L
data R
f :: IO (Maybe (E L))
f =
(undefined :: IO Bool) >>= case
False -> return (Just E1)
True ->
(undefined :: IO Bool) >>= case
False -> return (Just E2)
True ->
(undefined :: IO (Either L R)) >>= case
Left e -> return (Just (E3 e))
Right v -> (undefined :: R -> IO ()) v >> return Nothing
```

How to get rid of these three nested `case`

s?

Solution

Since you need to abort your computation early and return a value different from the final output, the `ExceptT`

transformer would be useful.

```
import Control.Monad (unless)
import Control.Monad.IO.Class
import Control.Monad.Trans.Except
data E a
= E1
| E2
| E3 a
data L
data R
f :: ExceptT (E L) IO ()
f = do
liftIO (undefined :: IO Bool) >>= flip unless (throwE E1)
liftIO (undefined :: IO Bool) >>= flip unless (throwE E2)
liftIO (undefined :: IO (Either L R)) >>=
either (throwE . E3) (liftIO . (undefined :: R -> IO ()))
```

If we want, we can then convert `ExceptT (E L) IO ()`

to `IO (Maybe (E L))`

:

```
f' :: IO (Maybe (E L))
f' = either Just (const Nothing) <$> runExceptT f
```

If could be further polished by using helper functions from `Control.Conditional`

or a similar library, and by extending the functions, for which `undefined`

fills in, to work within any `MonadIO`

(if that’s possible).

It may worth inventing your own combinators with semantically appropriate names. Here is an example using ExceptRT monad from errors package:

```
import Control.Monad.Trans (liftIO)
import Control.Error (rightMay, ExceptRT, runExceptT, runExceptRT, succeedT)
runE :: Functor m => ExceptRT a m e -> m (Maybe a)
runE = fmap rightMay . runExceptT . runExceptRT
report :: Monad m => a -> Bool -> ExceptRT a m ()
report res False = succeedT res
report _ _ = return ()
f :: IO (Maybe (E L))
f = runE $ do
liftIO foo >>= report E1
liftIO bar >>= report E2
liftIO baz >>= either (succeedT . E3) (liftIO . quxx)
```