Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>@Savanni D'Gerinel: you are on the right track. Let's extract your error-catching code from <em>verifyFibFile</em> to make it more generic, and modify it slightly so that it works directly in ErrorT:</p> <pre><code>catchError' :: ErrorT e IO a -&gt; (IOError -&gt; ErrorT e IO a) -&gt; ErrorT e IO a catchError' m f = ErrorT $ catchError (runErrorT m) (fmap runErrorT f) </code></pre> <p><em>verifyFibFile</em> can now be written as:</p> <pre><code>verifyFibFile' :: FilePath -&gt; FibIOMonad () verifyFibFile' path = do sequenceStr &lt;- catchError' (liftIO $ readFile path) (throwError . FileUnReadable) ErrorT . return $ verifySequence sequenceStr' (0, 0) </code></pre> <p>Notice what we have done in <em>catchError'</em>. We have stripped the ErrorT constructor from the <code>ErrorT e IO a</code> action, and also from the return value of the error-handling function, knowing than we can reconstruct them afterwards by wrapping the result of the control operation in ErrorT again.</p> <p>Turns out that this is a common pattern, and it can be done with monad transformers other than ErrorT. It can get tricky though (how to do this with ReaderT for example?). Luckily, the <a href="http://hackage.haskell.org/package/monad-control" rel="nofollow">monad-control</a> packgage already provides this functionality for many common transformers.</p> <p>The type signatures in monad-control can seem scary at first. Start by looking at just one function: <a href="http://hackage.haskell.org/packages/archive/monad-control/0.3.1.4/doc/html/Control-Monad-Trans-Control.html#v%3acontrol" rel="nofollow">control</a>. It has the type:</p> <pre><code>control :: MonadBaseControl b m =&gt; (RunInBase m b -&gt; b (StM m a)) -&gt; m a </code></pre> <p>Let's make it more specific by making <code>b</code> be <code>IO</code>:</p> <pre><code>control :: MonadBaseControl IO m =&gt; (RunInBase m IO -&gt; IO (StM m a)) -&gt; m a </code></pre> <p><em>m</em> is a monad stack built on top of <em>IO</em>. In your case, it would be <em>ErrorT IO</em>.</p> <p><code>RunInBase m IO</code> is a type alias for a magical function, that takes a value of type <code>m a</code> and returns a value of type <code>IO *something*</code>, <em>something</em> being some complex magic that encodes the state of the whole monad stack inside IO and lets you reconstruct the <code>m a</code> value afterwards, once you have "fooled" the control operation that only accepts IO values. <em>control</em> provides you with that function, and also handles the reconstruction for you.</p> <p>Applying this to your problem, we rewrite <em>verifyFibFile</em> once more as:</p> <pre><code>import Control.Monad.Trans.Control (control) import Control.Exception (catch) verifyFibFile'' :: FilePath -&gt; FibIOMonad () verifyFibFile'' path = do sequenceStr &lt;- control $ \run -&gt; catch (run . liftIO $ readFile path) (run . throwError . FileUnreadable) ErrorT . return $ verifySequence sequenceStr' (0, 0) </code></pre> <p>Keep in mind that this only works when the proper instance of <code>MonadBaseControl b m</code> exists. </p> <p><a href="http://www.yesodweb.com/blog/2011/08/monad-control" rel="nofollow">Here</a> is a nice introduction to monad-control.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload