bluefin-0.6.0.0: The Bluefin effect system
Safe HaskellNone
LanguageHaskell2010

Bluefin.Capability.Request

Synopsis

Documentation

Request allows to yield values and await the result. You might want to start with Bluefin.Capability.Yield, which is the most common way to use Requests.

Prompt finalization/resource safety

Bluefin Yield / Await / Request computations have much better resource safety properties than Conduit and Pipes. You can use Bluefin.Eff.bracket within a streaming computation and the acquired resource is guaranteed to be released and the end of the bracket, rather than at the end of the ResourceT scope as it is the case in Conduit and Pipes. See the blog post Bluefin streams finalize promptly for more details.

Running coroutines that communicate via Requests

Bluefin operations can be executed as coroutines using connectRequests (Wikipedia suggests that such coroutines are "second-class stackful coroutines"). Two coroutines run in this way communicate synchronously by using Requests to interact with a bi-directional channel. This means that such coroutines are often run exclusively for what they communicate via this channel, not for their return value.

Requests used in this way work a bit like UNIX pipes: there is a downstream consumer and an upstream generator. For every pair of such communicating coroutines there are two ends, represented with the capabilities Request a b and Request b a. The first parameter to Request is the type that can be sent from that end, while the second parameter is the type that will subsequently be received by that end. This explains the symmetry in the capabilities: what one end sends the other receives. The implication is that upstream and downstream exchange messages with each other at the same time. Additionally, there is a clear order of communication from the start (in Bluefin, communication is started by upstream).

request is the only effectful operation required: a Request a b capability that represents one end of a channel sends as and receives a bs. For many use cases, upstream does not need to know anything from downstream (dually: downstream does not need to communicate anything to upstream) except that downstream is making a new request, so the capabilities that describe most channels are "Request a ()" and "Request () a". Bluefin provides synonyms for these: Yield a and Await a, respectively. The specializations of request for Yield and Await are called yield and await. Coroutines that send data in only one direction like this can be created using awaitYield.

Because the message exchange occurs synchronously, when yielding, the upstream will block until the downstream awaits. The converse is also true: when downstream awaits, it will block until upstream yields.

Any Bluefin effectful operation that takes a Request capability as an argument can be run as coroutine using connectRequests by providing a second effectful operation as its counterpart on the other end of the channel.

For simple applications one may not need connectRequests at all, because specific handlers are already provided by Bluefin. See the "Handlers" sections of the Bluefin.Capability.Yield and Bluefin.Capability.Await modules.

Capability

type Request = Coroutine #

Capability to yield a value of type a and then await a value of type b in response.

Handlers

forEach #

Arguments

:: forall a b (es :: Effects) r. (forall (e1 :: Effects). Coroutine a b e1 -> Eff (e1 :& es) r) 
-> (a -> Eff es b)

Apply this effectful function for each element of the coroutine

-> Eff es r 

Apply an effectful function to each element yielded to the stream.

>>> runPureEff $ yieldToList $ \y -> do
      forEach (inFoldable [0 .. 3]) $ \i -> do
        yield y i
        yield y (i * 10)
([0, 0, 1, 10, 2, 20, 3, 30], ())

connectRequests #

Arguments

:: forall (es :: Effects) a b r. (forall (e :: Effects). Request a b e -> Eff (e :& es) r) 
-> (forall (e :: Effects). a -> Request b a e -> Eff (e :& es) r) 
-> Eff es r

͘

Interleave the execution of two Bluefin operations by sending their requests to each other. as are sent from the first to the second which responds by returning bs. The first runs until it yields its first a it starts the second (which is awaiting an a).

Effectful operations

request #

Arguments

:: forall (e1 :: Effects) (es :: Effects) a b. e1 <: es 
=> Request a b e1 
-> a

͘

-> Eff es b