How to do control flow in Haskell
<p>I'll give an example of what I want to do right away.</p>

<pre><code>version1 :: IO ()
version1 =
  if boolCheck
     then case maybeCheck of
            Nothing -&gt; putStrLn "Error: simple maybe failed"
            Just v  -&gt; case eitherCheck of
                         Left  e -&gt; putStrLn $ "Error: " ++ show e
                         Right w -&gt; monadicBoolCheck v &gt;&gt;= \case
                                      False -&gt; putStrLn "Error: monadic bool check failed"
                                      True  -&gt; print "successfully doing the thing"
    else putStrLn "simple bool check failed"
</code></pre>

<p>Basically I want to "do a thing" under the condition that a number of checks turns out positive.
Whenever a single check turns out negative, I want to preserve the information about the offending check and abort the mission.
In real life those checks have different types, therefore I called them</p>

<pre><code>boolCheck        :: Bool
maybeCheck       :: Maybe a
eitherCheck      :: Show a =&gt; Either a b
monadicBoolCheck :: Monad m =&gt; m Bool
</code></pre>

<p>Those are just examples.
Feel free to also think of monadic Maybe, <code>EitherT</code> or a a singleton list where I extract <code>head</code> and fail when it is not a singleton.</p>

<p>Now I am trying to improve the above implementation and the <code>Either</code> monad came into my mind, because it has the notion of aborting with an error message.</p>

<pre><code>version2 :: IO ()
version2 = do
  result &lt;- runEitherT $ do
    if boolCheck
       then pure ()
       else left "simple bool check failed"
    v &lt;- case maybeCheck of
           Just x  -&gt; pure x
           Nothing -&gt; left "simple maybe check failed"
    w &lt;- hoistEither . mapLeft show $ eitherCheck
    monadicBoolCheck v &gt;&gt;= \case
      True  -&gt; pure ()
      False -&gt; left  "monadic bool check failed"
  case result of
    Left  msg -&gt; putStrLn $ "Error: " ++ msg
    Right _   -&gt; print "successfully doing the thing"
</code></pre>

<p>While I prefer <code>version2</code>, the improvement in readability is probably marginal.
Version2 is superior when it comes to adding further checks.</p>

<p><strong>Is there an ultimately elegant way of doing this?</strong></p>

<p>What I don't like:</p>

<p>1) I am partly abusing the <code>Either</code> monad and what I actually do is more like a <code>Maybe</code> monad with the rolls of <code>Just</code> and <code>Nothing</code> switched in the monadic bind</p>

<p>2) The conversion of the checks to <code>Either</code> requires either rather verbose use of <code>case</code> or a conversion function (like <code>hoistEither</code>).</p>

<p>Ways of improving readability might be:</p>

<p>1) define helper functions to allow code like</p>

<pre><code>v &lt;- myMaybePairToEither "This check failed" monadicMaybePairCheck

monadicMaybePairCheck :: Monad m =&gt; m (Maybe x, y)
...
myMaybePairToEither :: String -&gt; m (Maybe x, y) -&gt; EitherT m e z
myMaybePairToEither _   (Just x, y)  = pure $ f x y
myMaybePairToEither msg (Nothing, _) = left msg
</code></pre>

<p>2) consistently use explicit cases, not even use <code>hoistEither</code></p>

<p>3) defining my own monad to stop the <code>Either</code> abuse ... I could provide all the conversion functions along with it (if no-one has already done something like that)</p>

<p>4) use <code>maybe</code> and <code>either</code> where possible</p>

<p>5) ... ?</p>