An error slipped into the version below.  The line:

E.checkDone (E.continue . dotIn (count - len))

should read

E.checkDone (E.continue . dotIn (need - len))



"David Hotham" <[email protected]> wrote in message news:[email protected]...
Hello,

I've spent some time over the last couple of days trying to write an enumeratee that prints a "." every n bytes (with obvious intended use as a progress tracker). Seems like it oughtn't be hard, but it has been a steep learning curve...

I have come up with something that seems to do the job but I don't know that I'm completely happy with it (or even that I completely understand it, to be honest).

If anyone more expert would be kind enough either to reassure me that I'm doing it right or - more likely - to offer improvements / suggestions on what obvious simplifications I have overlooked, I'd be grateful.

Thanks

David


import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as BL
import qualified Data.Enumerator as E
import qualified Data.Enumerator.Binary as EB
import Control.Monad.IO.Class (liftIO, MonadIO)
import System.IO (hFlush, stdout)


dotEvery :: MonadIO m => Integer -> E.Enumeratee B.ByteString B.ByteString m b
dotEvery count = E.checkDone $ E.continue . dotIn count where
  dotIn need k E.EOF = E.yield (E.Continue k) E.EOF
  dotIn need k (E.Chunks []) = E.continue (dotIn need k)
  dotIn need k (E.Chunks xs) = iter where
    lazy = BL.fromChunks xs
    len = toInteger $ BL.length lazy
    iter = if len < need
              then k (E.Chunks xs) E.>>==
                    E.checkDone (E.continue . dotIn (count - len))
           else let (x1, x2) = BL.splitAt (fromInteger need) lazy
                    s1 = E.Chunks $ BL.toChunks x1
                    s2 = E.Chunks $ BL.toChunks x2
                    enumee = E.checkDoneEx s2 (\k' -> dotIn count k' s2)
                in E.Iteratee $ do newStep <- E.runIteratee $ k s1
                                   liftIO $ putStr "." >> hFlush stdout
                                   E.runIteratee $ enumee newStep



PS Implementations which involve "EB.take count" seem to me unsatisfactory; one surely oughtn't need to have a large buffer to solve this problem PPS I did find an implementation via mapAccumM which I consider pleasing enough from an aesthetic point of view - but which runs 30x slower

dotAt :: MonadIO m => Integer -> Integer -> Word8 -> m (Integer, Word8)
dotAt n s w | s >= n    = do liftIO $ putStr "." >> hFlush stdout
                            return (1, w)
           | otherwise = return (s+1, w)

dotEvery' :: MonadIO m => Integer -> E.Enumeratee B.ByteString B.ByteString m b dotEvery' n = EB.mapAccumM (dotAt n) 1



_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to