Also, I forgot to mention that there is a warm and fuzzy implementation of `Handle`s using `pipes`, which I originally wrote about here:

http://www.haskellforall.com/2013/04/pipes-and-io-streams.html

That post was written using the old and ugly `pipes-3.*` API, but I can summarize it using the new API pretty briefly:

Read only handles have type:

    readHandle :: Effect IO a  -- Read a value of type `a`

Transformations on read-only handles have types like:

    readTransformation1 :: Consumer a IO b
    readTransformation2 :: Consumer b IO c

You use `(>~)` connect transformations to handles:

    (>~) :: Effect IO a -> Consumer a IO b -> Effect IO b

    readHandle >~ readTransformation1 :: Effect IO b

... and also to connect transformations to each other:

    (>~) :: Consumer a IO b -> Consumer b IO c -> Consumer a IO c

    readTransformation1 >~ readTransformation2 :: Consumer a IO c

This connection process is associative:

    (readHandle >~ readTransformation1) >~ readTransformation2
    = readHandle >~ (readTransformation1 >~ readTransformation2)
        :: Effect IO c

... and `await` is the identity handle/transformation:

    await :: Consumer a IO a

Dually, write handles have type:

    writeHandle :: c -> Effect IO ()

... and transformations on write handle have types like:

    writeTransformation1 :: a -> Producer b IO ()
    writeTransformation2 :: b ->Producer c IO ()

You use `(~>)` to connect transformations to handles:

(~>) :: (b -> Producer c IO ()) -> (c -> Effect IO ()) -> (b -> Effect IO ())

    writeTransformation1 ~> writeHandle :: b -> Effect IO ()

... and also to connect transformations to each other:

(~>) :: (a -> Producer b IO ()) -> (b -> Producer c IO ()) -> (a -> Producer c IO ())

    writeTransformation1 ~> writeTransformation2 :: a -> Producer c IO ()

This connection process is associative:

    (writeTransformation1 ~> writeTransformation2) ~> writeHandle
    = writeTransformation1 ~> (writeTransformation2 ~> writeHandle)
        :: a -> Effect IO ()

... and `yield` is the identity handle/transformation:

    yield :: a -> Producer a IO ()

Also, these generalize to any base monad (not just `IO`) and they work bidirectionally, too, if you just replace them all with their `Pipes.Core` analogs (i.e. `request`/`respond`/`(/>/)`/`(\>\)`). This means that you can have write handles that return a result and read handles which take an argument.

I think I will just go ahead and write this up into a `pipes-handle` package. It actually plays pretty nicely with the rest of the `pipes` ecosystem.

On 02/02/2014 07:06 AM, Jeremy Shaw wrote:
On Thu, Jan 30, 2014 at 5:03 PM, Dan Burton <[email protected]> wrote:

It seems that you are treating Producers like you would treat a handle. And
indeed, with "resumable", you can upgrade any Producer to a handle-like
Producer that retains its position across pipelines.
Yes! The Handle analogy is exactly the description I was looking for.

But the whole advantage of the Pipes/Conduits/Iteratee abstraction over
handles is that the composition operators abstract away this "position"
stuff. As far as I can tell, idiomatic Pipes code typically only uses a
producer in exactly one pipeline.
Right -- this is my feeling as well. I *can* write code using
Producers like Handles -- but is it wrong to? It sure doesn't make me
feel warm and fuzzy inside.

- jeremy


--
You received this message because you are subscribed to the Google Groups "Haskell 
Pipes" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].

Reply via email to