Question

Having a producer of type Producer ByteString IO () and a pipe of type Pipe ByteString a IO () how do I compose an effect, which will result in IO a when run?

Here's my best attempt:

{-# LANGUAGE ScopedTypeVariables #-}
import Pipes
import Data.ByteString

run :: forall a. IO a
run = runEffect $ 
  (undefined :: Producer ByteString IO ()) >-> (undefined :: Pipe ByteString a IO ())

It fails with the following:

Couldn't match type `Void' with `()'
Expected type: IO a
  Actual type: IO ()
In the expression:
  runEffect
  $ (undefined :: Producer ByteString IO ())
    >-> (undefined :: Pipe ByteString a IO ())
In an equation for `run':
    run
      = runEffect
        $ (undefined :: Producer ByteString IO ())
          >-> (undefined :: Pipe ByteString a IO ())
Was it helpful?

Solution

Generally, you need to compose a Producer with a Consumer in order to get an Effect which can be run by runEffect. That's not what you've got here, but fortunately, there are more ways to eliminate a Proxy than just runEffect.

Taking stock of what we have, this composition ends up with a Producer.

pipe :: Producer a IO ()
pipe = (undefined :: Producer ByteString IO ()) >-> (undefined :: Pipe ByteString a IO ()) 

The Pipes.Prelude module contains many other ways to eliminate Producers like Pipes.Prelude.last

last :: Monad m => Producer a m () -> m (Maybe a)

Probably the most general way to get as out is to use Pipes.Prelude.fold

fold :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Producer a m () -> m b

which is like runEffect except it reduces Producers to their underlying Monad. Since that's what we have it'll work great. Here's how we can implement Pipes.Prelude.head

slowHead = fold (\res a -> res <> First (Just a)) mempty getFirst

Though it's worth noting that slowHead consumes the entire Producer (and thus performs all of the needed effects) while Pipes.Prelude.head performs just the first one. It's much lazier!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top