Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The way <code>pipes</code> does it is to type-class composition using the <code>Channel</code> type class:</p> <pre class="lang-hs prettyprint-override"><code>class Channel p where {-| 'idT' acts like a \'T\'ransparent proxy, passing all requests further upstream, and passing all responses further downstream. -} idT :: (Monad m) =&gt; a' -&gt; p a' a a' a m r {-| Compose two proxies, satisfying all requests from downstream with responses from upstream. -} (&gt;-&gt;) :: (Monad m) =&gt; (b' -&gt; p a' a b' b m r) -&gt; (c' -&gt; p b' b c' c m r) -&gt; (c' -&gt; p a' a c' c m r) p1 &gt;-&gt; p2 = p2 &lt;-&lt; p1 </code></pre> <p>... and derived a lifted composition over <code>EitherT</code> from the base composition. This is a special case of the the principle of proxy transformers, introduced in <code>pipes-2.4</code>, that allows lifting composition over arbitrary extensions.</p> <p>This lifting requires defining an <code>EitherT</code> specialized to the shape of the <code>Proxy</code> type in <code>Control.Proxy.Trans.Either</code>:</p> <pre class="lang-hs prettyprint-override"><code>newtype EitherP e p a' a b' b (m :: * -&gt; *) r = EitherP { runEitherP :: p a' a b' b m (Either e r) } </code></pre> <p>This specialization to the <code>Proxy</code> shape is necessary in order to be able to define a well-typed instance of the <code>Channel</code> class. Scala might be more flexible in this regard than Haskell.</p> <p>Then I just redefine the <code>Monad</code> instance (and other instances) along with all the ordinary <code>EitherT</code> operations for this specialized type:</p> <pre class="lang-hs prettyprint-override"><code>throw :: (Monad (p a' a b' b m)) =&gt; e -&gt; EitherP e p a' a b' b m r throw = EitherP . return . Left catch :: (Monad (p a' a b' b m)) =&gt; EitherP e p a' a b' b m r -- ^ Original computation -&gt; (e -&gt; EitherP f p a' a b' b m r) -- ^ Handler -&gt; EitherP f p a' a b' b m r -- ^ Handled computation catch m f = EitherP $ do e &lt;- runEitherP m runEitherP $ case e of Left l -&gt; f l Right r -&gt; right r </code></pre> <p>With this in hand I can then define the following lifted composition instance:</p> <pre class="lang-hs prettyprint-override"><code>-- Given that 'p' is composable, so is 'EitherP e p' instance (Channel p) =&gt; Channel (EitherP e p) where idT = EitherP . idT p1 &gt;-&gt; p2 = (EitherP .) $ runEitherP . p1 &gt;-&gt; runEitherP . p2 </code></pre> <p>To understand what is going on there, just follow the types:</p> <pre class="lang-hs prettyprint-override"><code>p1 :: b' -&gt; EitherP e p a' a b' b m r p2 :: c' -&gt; EitherP e p b' b c' c m r runEitherP . p1 :: b' -&gt; p a' a b' b m (Either e r) runEitherP . p2 :: c' -&gt; p b' b c' c m (Either e r) -- Use the base composition for 'p' runEitherP . p1 &gt;-&gt; runEitherP . p2 :: c' -&gt; p a' a c' c m (Either e r) -- Rewrap in EitherP (EitherP . ) $ runEitherP . p1 &gt;-&gt; runEitherP . p2 :: c' -&gt; EitherP e p a' a c' c m r </code></pre> <p>This lets you throw and catch errors within a particular stage without interrupting other stages. Here's an example I've copied and pasted from my <code>pipes-2.4</code> announcement post:</p> <pre class="lang-hs prettyprint-override"><code>import Control.Monad (forever) import Control.Monad.Trans (lift) import Control.Proxy import Control.Proxy.Trans.Either as E import Safe (readMay) promptInts :: () -&gt; EitherP String Proxy C () () Int IO r promptInts () = recover $ forever $ do str &lt;- lift getLine case readMay str of Nothing -&gt; E.throw "Could not parse an integer" Just n -&gt; liftP $ respond n recover p = p `E.catch` (\str -&gt; lift (putStrLn str) &gt;&gt; recover p) main = runProxy $ runEitherK $ mapP printD &lt;-&lt; promptInts </code></pre> <p>Here's the result:</p> <pre class="lang-hs prettyprint-override"><code>&gt;&gt;&gt; main 1&lt;Enter&gt; 1 Test&lt;Enter&gt; Could not parse an integer Apple&lt;Enter&gt; Could not parse an integer 5&lt;Enter&gt; 5 </code></pre> <p>The answer to the iteratee approach is similar. You must take your existing way of composing iteratees and lift it over <code>EitherT</code>. Whether or not you use type-classes or just define a new composition operator is up to you.</p> <p>Some other useful links:</p> <ul> <li><a href="http://www.haskellforall.com/2012/10/pipes-24-proxy-transformers-extra.html" rel="nofollow noreferrer"><code>pipes-2.4</code> announcement post</a></li> <li><a href="http://hackage.haskell.org/packages/archive/pipes/2.5.0/doc/html/Control-Proxy-Class.html" rel="nofollow noreferrer"><code>Control.Proxy.Class</code></a>, <a href="http://hackage.haskell.org/packages/archive/pipes/2.5.0/doc/html/Control-Proxy-Trans.html" rel="nofollow noreferrer"><code>Control.Proxy.Trans</code></a>, and <a href="http://hackage.haskell.org/packages/archive/pipes/2.5.0/doc/html/Control-Proxy-Trans-Either.html" rel="nofollow noreferrer"><code>Control.Proxy.Trans.Either</code></a></li> <li>A <a href="https://stackoverflow.com/questions/11402892/what-approach-to-error-handling-to-use-with-pipes-core/11417819#11417819">very similar Stack Overflow question</a> on the same subject (except for `pipes)</li> </ul>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

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