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].