Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The <code>pipe</code> abstraction does not support diamond topologies or any form of <code>Arrow</code>-like behavior. This is not an API issue, but rather there is no correct or well-defined behavior for such a scenario.</p> <p>To explain why, allow me to simplify your diagram to the following one:</p> <pre><code> +----+ | pL | +----+ =&gt; +----+ =&gt; +----+ | p1 | | p2 | +----+ =&gt; +----+ =&gt; +----+ | pR | +----+ </code></pre> <p>Imagine we are at the <code>p1</code> pipe and we <code>respond</code> to <code>pL</code>. If you remember the tutorial, the proxy laws require that every <code>respond</code> blocks until upstream. That means that <code>p1</code> cannot regain control until <code>pL</code> <code>request</code>s again. So at this point we have:</p> <ul> <li><code>p1</code> blocked waiting for a <code>request</code> from <code>pL</code></li> </ul> <p>However, suppose that <code>pL</code> does not <code>request</code> yet and instead <code>respond</code>s with a value of its own to <code>p2</code>. So now we have:</p> <ul> <li><code>p1</code> blocked waiting for a <code>request</code> from <code>pL</code></li> <li><code>pL</code> blocked waiting for a <code>request</code> from <code>p2</code></li> </ul> <p>Now suppose that <code>p2</code> instead <code>request</code>s from <code>pR</code>. The proxy laws say that <code>p2</code> cannot regain control until <code>pR</code> <code>respond</code>s again. Now we have:</p> <ul> <li><code>p1</code> blocked waiting for a <code>request</code> from <code>pL</code></li> <li><code>pL</code> blocked waiting for a <code>request</code> from <code>p2</code></li> <li><code>p2</code> blocked waiting for a <code>respond</code> from <code>pR</code></li> </ul> <p>Now what happens when <code>pR</code> <code>request</code>s a value from <code>p1</code>? If we consult our list of blocks, <code>p1</code> is still blocked waiting for a <code>request</code> from <code>pL</code>, so it is in no shape to receive a <code>request</code> from <code>pR</code>. There is no correct way to "tie the knot", so to speak, even if <code>pL</code> and <code>pR</code> shared the same <code>request</code> signature.</p> <p>More generally, the proxy laws ensure the following two invariants:</p> <ul> <li>Every pipe "upstream" of the active pipe will be blocked on a <code>respond</code></li> <li>Every pipe "downstream" of the acive pipe will be blocked on a <code>request</code></li> </ul> <p>Cycles or diamonds break these invariants. This is why the tutorial very briefly remarks in passing that cyclic topologies do not "make sense".</p> <p>You can see why diamonds break this invariant in the example I just gave you. When <code>p1</code> had control it was upstream of <code>pR</code>, which would imply <code>pR</code> was blocked on a <code>request</code>. However, when <code>p2</code> gained control it was downstream of <code>pR</code>, which would imply <code>pR</code> was blocked on a <code>respond</code>. This leads to a contradiction, because <code>pR</code> couldn't have changed yet since control flowed through <code>pL</code> and not <code>pR</code> to get to <code>p2</code>.</p> <h2>Machines</h2> <p>So there are two solutions to your problem. one solution is to just inline your desired splitting behavior into a single pipe. You define a <code>pE</code> pipe that combines the behavior of <code>pL</code> and <code>pR</code> into a single pipe.</p> <p>The more elegant solution to this problem is something in the style of Edward's <code>machines</code>. You define a more restricted abstraction that is less powerful than proxies that supports <code>ArrowChoice</code>, you do your arrow-ish stuff within the domain of that abstraction, and then when you are done you upgrade it to proxies.</p> <p>If you squint, you could pretend that there is a category of currently available coroutine abstractions in Haskell that is a partial order. Coroutines abstractions are the objects, and an arrow from coroutine abstraction <code>C1</code> to coroutine abstraction <code>C2</code> means that you can embed coroutines of type <code>C1</code> in coroutines of type <code>C2</code> (i.e. <code>C1</code> is an improper subset of <code>C2</code>).</p> <p>In this partial order, proxies would probably be the terminal object, meaning that you can think of proxies as the <strong>assembly language of coroutines</strong>. Following the analogy of assembly language, proxies provide less guarantees, but you can embed more restrictive coroutine abstractions (i.e. higher-level languages) within proxies. These higher-level languages provide greater restrictions which enables more powerful abstractions (i.e. an <code>Arrow</code> instance).</p> <p>If you want a trivial example of this, consider one of the simplest coroutine abstractions: the Kleisli arrow:</p> <pre><code>newtype Kleisli m a b = Kleisli { runKleisli :: a -&gt; m b } instance Category (Kleisli m) where id = Kleisli return (Kleisli f) . (Kleisli g) = Kleisli (f &lt;=&lt; g) </code></pre> <p>Kleisli arrows are definitely more restrictive than proxies, but because of this restriction they support an <code>Arrow</code> instance. So whenever you need an <code>Arrow</code> instance you write your code using Kleisli arrows, and combine it using <code>Arrow</code> notation, and then when you are done, you can "compile" that higher-level Kleisli code to the proxy assembly code using <code>mapMD</code>:</p> <pre><code>kleisliToProxy :: (Proxy p) =&gt; Kleisli m a b -&gt; () -&gt; Pipe p a b m r kleisliToProxy (Kleisli f) = mapMD f </code></pre> <p>This compilation obeys the functor laws:</p> <pre><code>kleisliToProxy id = idT kleisliToProxy (f . g) = kleisliToProxy f &lt;-&lt; kleisliToProxy g </code></pre> <p>So if your branching code can be written in terms of <code>Kleisli</code> arrows, then use <code>Kleisli</code> arrows for that section of the code and then compile it down to proxies when you are done. Using this trick, you can compile multiple coroutine abstractions down to the proxy abstraction to mix them.</p>
    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. 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.
 

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