Use `Pipes.Prelude.fold`, which has this type:

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

The trick is realizing that this works if we specialize the `a`, `x`, and `b` type parameters to:

    a = Block
    x = Either String BlockState
    b = Either String BlockState

95% of the work is just figuring out what the type of your accumulator (i.e. the type parameter`x`) should be. Once you figure that out it's all downhill from there.

... and also specialize `m` to `IO`, which would give us:

    fold
        :: (Either String BlockState -> Block -> Either String BlockState)
-> Either String BlockState
        -> (Either String BlockState -> Either String BlockState)
        -> Producer Block IO () -> IO (Either String BlockState)

That means that we need to pass `fold` three functions of type:

    step  :: Either String BlockState -> Block -> Either String BlockState
    begin :: Either String BlockState
    done  :: Either String BlockState -> Either String BlockState

fold step begin done :: Producer Block IO () -> IO (Either String BlockState)

We can create the `step` function from your `processBlock` function:

    step :: Either String BlockState -> Block -> Either String BlockState
    step e block = do
        blockState <- e
        process blockState block

The `done` function is really easy:

    done :: Either String BlockState -> Either String BlockState
    done = id

... and the `begin` function requires us to supply some sort of beginning state:

    begin :: Either String BlockState
begin = return initialBlockState -- We'll get `initialBlockState` elsewhere

... so we can combine those together to write up the complete function:

foldBlocks :: BlockState -> Producer Block IO () -> IO (Either String BlockState)
    foldBlocks initialBlockState = Pipes.Prelude.fold step begin done
      where
        step e block = do
            blockState <- e
            process blockState block

        begin = return initialBlockState

        done = id

Now to give a high-level explanation of what is going on.

You probably tried to originally solve this by using `Pipes.Prelude.foldM` but you noticed that the `Either` monad didn't match up with the `Producer`'s base monad, `IO`. However, you can still use `Pipes.Prelude.fold` if you do all the `Either` monad work within the fold itself. In other words, the step function does the binding instead of relying on the fold to do the binding for you.

On 9/30/15 8:40 PM, Rune Kjær Svendsen wrote:
Hello list,

I have a Producer of the following type:

    blockProducer :: Handle -> Producer Block IO ()

which reads Blocks from a file with the given handle, and yields a block one-by-one until EOF is reached.

I also have a function of the following type:

    processBlock :: BlockState -> Block -> Either String BlockState

which takes an initial BlockState, and updates it with Block, and returns either Right BlockState if no error occurs, or Left String if an error occurs.

So I want to create a Pipe that consumes Blocks from blockProducer, folds them as if I were using (foldlM processBlock), and yields the resulting Either String BlockState.

How do I do that?
--
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 haskell-pipes+unsubscr...@googlegroups.com <mailto:haskell-pipes+unsubscr...@googlegroups.com>. To post to this group, send email to haskell-pipes@googlegroups.com <mailto:haskell-pipes@googlegroups.com>.

--
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 haskell-pipes+unsubscr...@googlegroups.com.
To post to this group, send email to haskell-pipes@googlegroups.com.

Reply via email to