Dear Rune, 

This might be the magic moment for shifting to the `foldl` library (`cabal 
install foldl`). Then you can keep pretty much everything you have
from above, but slide in other independent components like logging or 
debugging and other stuff, e.g. a final count of blocks processed, 
as you please. Part of the reason `Pipes.Prelude.fold` has the type it has 
it to make it interoperate with 'Control.Foldl`.

To use the library, instead of directly applying `Pipes.Prelude.fold` to 
`step` `begin` and
`done` defined above, you will 'reify' the fold, to start with. That is, 
instead of writing 


    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

you will write 

    
     blockFold : BlockState -> Control.Foldl.Fold Block (Either String 
BlockState)
     blockFold initialBlockState = Control.Foldl.Fold step begin done
      where
        step e block = do
            blockState <- e
            process blockState block

        begin = return initialBlockState

        done = id

then the function defined earlier is recovered with a use of `purely` from 
Control.Foldl

    foldBlocks :: BlockState -> Producer Block IO () -> IO (Either String 
BlockState)
    foldBlocks initialBlockState = Control.Foldl.purely Pipes.Prelude.fold 
(blockFold initialBlockState)

`purely` just extracts the components of the 'reified fold' and feeds them 
to a suitably
defined fold from another library. ( Here, it is the pipes fold function, 
but there are 
similar functions elsewhere like say 
http://hackage.haskell.org/package/mono-traversable-0.9.3/docs/Data-MonoTraversable.html#v:ofoldlUnwrap
 
)

So far our results are as before, but with this little fold reifying bit. 
Once we've expressed
our `foldBlocks` with a `Control.Foldl.Fold`, we can start adding stuff. 
For example given 


    Control.Foldl.length :: Fold a Int

we can immediately complicate `foldBlocks` to also give a count of blocks 
processed.

    blockFoldWithLength : BlockState -> Control.Foldl.Fold Block 
(Int,Either String BlockState)
    blockFoldWithLength initialState = liftA2 (,) Control.Foldl.length 
(blockFold initialState) 

so now we can trivial alter our processing function to 



    foldBlocksWithLength :: BlockState -> Producer Block IO () -> IO (Int, 
Either String BlockState)
    foldBlocksWithLength initialBlockState = 
            Control.Foldl.purely Pipes.Prelude.fold (blockFoldWithLength 
initialBlockState)


But the problem wasn't to snap a pure component like block count into our 
fold, but to snap in
an impure logging component. So we are using the `FoldM` type from 
`Control.Foldl`, not the `Fold` type.
No problem, we already have a `FoldM` on our hands:

     blockFoldM : Monad m => BlockState -> Control.Foldl.FoldM m Block 
(Either String BlockState)
     blockFoldM initialState = generalize (blockFold initialState) 


and we could as well have written our initial `foldBlocks` as: 

    foldBlocks :: BlockState -> Producer Block IO () -> IO (Either String 
BlockState)
    foldBlocks initialBlockState = Control.Foldl.impurely 
Pipes.Prelude.foldM (blockFoldM initialBlockState)

but now we have it in a shape where we can snap in a logger function. We 
might do this
by hand writing something with this shape

     loggerFold :: Handle -> Control.FoldM IO Block ()
     loggerFold = FoldM step begin done where
        step x block = undefined -- new 'IO x' whatever it is, probably IO 
()
        begin = undefined        -- initial IO x, probably 'return ()'
        done _ = return ()       -- given I assumed () in the signature


So it is a question of writing `step` to do whatever you want to be logged 
from each block.
Then you will just write:


    foldBlocksWithLogging :: BlockState -> Producer Block IO () -> IO 
(Either String BlockState)
    foldBlocksWithLogging initialBlockState = 
        Control.Foldl.impurely Pipes.Prelude.foldM (blockFoldM 
initialBlockState <* loggerFold)

or

      foldBlocksWithLogging :: BlockState -> Producer Block IO () -> IO 
(Either String BlockState)
    foldBlocksWithLogging initialBlockState = 
        Control.Foldl.impurely Pipes.Prelude.foldM (generalize (blockFold 
initialBlockState) <* loggerFold)

a sufficiently trivial logger can be written with 

    L.sink :: (Monad m, Monoid w) => (a -> m w) -> L.FoldM m a w

the ultra-minimal debugging fold would be 

        blockAlertFoldM :: FoldM IO Block ()
        blockAlertFoldM = sink (\block -> putStrLn "Block Processed!")

then we have, e.g.

    foldBlocksWithAlert :: BlockState -> Producer Block IO () -> IO (Either 
String BlockState)
    foldBlocksWithAlert initialBlockState = 
        Control.Foldl.impurely Pipes.Prelude.foldM (generalize (blockFold 
initialBlockState) <* loggerFold)


and could start throwing in other components


    foldBlocksWithAlertAndLength :: BlockState -> Producer Block IO () -> 
IO (Int, Either String BlockState)
    foldBlocksWithAlertAndLengh initialBlockState = Control.Foldl.impurely 
Pipes.Prelude.foldM myfolds where
         myfolds = generalize mypurefolds <* loggerFold
         mypurefolds = liftA2 (,) Control.Foldl.length (blockFold 
initialBlockState) 

     
Here we get the block count at the end, and are printing "Block processed!" 
to stdout as each block is processed.

There are probably a few gruesome type and typing errors in the above, but 
I hope it makes the Control.Foldl approach
to your problem clear. I probably managed to cross some crucial 
consideration of importance to Gabriel, but it's always pleasing to find 
out...


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