Reputation: 43310
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 ())
Upvotes: 3
Views: 274
Reputation: 74344
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 a
s 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!
Upvotes: 4