Re: [Haskell-cafe] IterIO: How to write use my inumReverse

2011-07-11 Thread Maciej Wos
Sorry, my previous message got truncated.

I was trying to say that many iteratees like iterReverse can be
defined nicely using combinators from Control.Applicative. You end up
with much cleaner code.

Also, iterLines doesn't work as the name would suggest. It only
consumes one line from the input stream and returns it inside a
singleton list.

Instead, iterLines can be defined like this:

iterLines :: (Monad m) = Iter L.ByteString m [L.ByteString]
iterLines = do
el - tryBI lineI
case el of
-- no more full lines left, return the remaining data
Left (e :: SomeException) - (:[]) $ dataI
-- read one line; add it to the list and read more
Right line - (line:) $ iterLines

enumPure line1\nline2\nline3 |$ iterLines
[Chunk line1 Empty,Chunk line2 Empty,Chunk line3 Empty]

However, the above returns the resulting list only after consuming the
whole stream, which is something to avoid.

In case of iterReverse it is better to read and accumulate characters
until \n is found and then return the reversed string. One way to do
this is to read one character at a time:

reverseLineSlow :: Iter L.ByteString IO L.ByteString
reverseLineSlow = iter 
where
iter acc = do
c - headI
case c of
10 - return $ L.reverse acc
_  - iter (acc `mappend` L.singleton c)

But this will be really slow. Instead, the data should be read one
chunk at a time:

reverseLine :: Iter L.ByteString IO L.ByteString
reverseLine = iter 
where
iter acc = do
-- read the data from the stream one chunk at a time
Chunk c eof - chunkI
-- check if there is any \n, i.e. if we read a whole line
let (a,b) = L.break (==10) c
if b == 
-- haven't found any \n yet; append all data to the accumulator
then iter (acc `mappend` a)
-- have found \n
else do
-- put the data after \n back (while removing \n itself)
ungetI $ L.tail b
-- return reversed accumulator plus the data up to \n
return $ L.reverse $ acc `mappend` a

Hope this helps!

-- Maciej

On Tue, Jul 12, 2011 at 12:47 AM, Maciej Wos maciej@gmail.com wrote:
 Don't forget Applicative instance!
 iterReverse = L.reverse $ lineI

 On Monday, 4 July 2011 at 22:54, dm-list-haskell-c...@scs.stanford.edu
 wrote:

 At Mon, 4 Jul 2011 20:36:33 +1000,
 John Ky wrote:

 Hi Haskell Cafe,

       enum |$ inumLines .| inumReverse .| inumUnlines .| iter
 ...

 iterLines :: (Monad m) = Iter L.ByteString m [L.ByteString]
 iterLines = do
   line - lineI
   return [[line]

 iterUnlines :: (Monad m) = Iter [L.ByteString] m L.ByteString
 iterUnlines = (L.concat . (++ [C.pack \n])) `liftM` dataI

 iterReverse :: (Monad m) = Iter [L.ByteString] m [L.ByteString]
 iterReverse = do
   lines - dataI
   return (map L.reverse lines)

 inumLines = mkInum iterLines
 inumUnlines = mkInum iterUnlines
 inumReverse = mkInum iterReverse

 It all works fine.

 My question is: Is it possible to rewrite inumReverse to be this:

 iterReverse :: (Monad m) = Iter L.ByteString m L.ByteString
 iterReverse = do
   line - dataI
   return (L.reverse line)

 inumReverse = mkInum iterReverse

 And still be able to use it in the line:

 enum |$ inumLines .| {-- inumReverse goes in here somehow --} .|
 inumUnlines .| iter

 The reason I ask is that the Haskell function reverse has the type [a] -
 [a],
 not  [[a]] - [[a]].

 I thought perhaps the alternative inumReverse is cleaner than the original
 as
 it behaves more similarly to Haskell's own reverse function.

 I'm not sure what you are trying to achieve. If you want an iter that
 works on L.ByteStrings, then you can say:

 iterReverse :: (Monad m) = Iter L.ByteString m L.ByteString
 iterReverse = do
 line - lineI
 return (L.reverse line)

 In that case you don't need inumLines and inumUnlines. If, however,
 you want the type to be [L.ByteString], and you would rather do this
 one line at a time, instead of calling map, then you could do
 something like the following:

 iterReverse :: (Monad m) = Iter [L.ByteString] m [L.ByteString]
 iterReverse = do
 line - headI
 return [L.reverse line]

 But the code you have above should also work, so it all depends on
 what you are trying to achieve.

 David

 ___
 Haskell-Cafe mailing list
 Haskell-Cafe@haskell.org
 http://www.haskell.org/mailman/listinfo/haskell-cafe



___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


Re: [Haskell-cafe] IterIO: How to write use my inumReverse

2011-07-04 Thread dm-list-haskell-cafe
At Mon, 4 Jul 2011 20:36:33 +1000,
John Ky wrote:
 
 Hi Haskell Cafe,
 
       enum |$ inumLines .| inumReverse .| inumUnlines .| iter
 ...

 iterLines :: (Monad m) = Iter L.ByteString m [L.ByteString]
 iterLines = do
   line - lineI
   return [line]

 iterUnlines :: (Monad m) = Iter [L.ByteString] m L.ByteString
 iterUnlines = (L.concat . (++ [C.pack \n])) `liftM` dataI

 iterReverse :: (Monad m) = Iter [L.ByteString] m [L.ByteString]
 iterReverse = do
   lines - dataI
   return (map L.reverse lines)

 inumLines = mkInum iterLines
 inumUnlines = mkInum iterUnlines
 inumReverse = mkInum iterReverse
 
 It all works fine.
 
 My question is: Is it possible to rewrite inumReverse to be this:
 
 iterReverse :: (Monad m) = Iter L.ByteString m L.ByteString
 iterReverse = do
   line - dataI
   return (L.reverse line)

 inumReverse = mkInum iterReverse
 
 And still be able to use it in the line:
 
 enum |$ inumLines .| {-- inumReverse goes in here somehow --} .|
 inumUnlines .| iter
 
 The reason I ask is that the Haskell function reverse has the type [a] - [a],
 not  [[a]] - [[a]].
 
 I thought perhaps the alternative inumReverse is cleaner than the original as
 it behaves more similarly to Haskell's own reverse function.

I'm not sure what you are trying to achieve.  If you want an iter that
works on L.ByteStrings, then you can say:

 iterReverse :: (Monad m) = Iter L.ByteString m L.ByteString
 iterReverse = do
   line - lineI
   return (L.reverse line)

In that case you don't need inumLines and inumUnlines.  If, however,
you want the type to be [L.ByteString], and you would rather do this
one line at a time, instead of calling map, then you could do
something like the following:

 iterReverse :: (Monad m) = Iter [L.ByteString] m [L.ByteString]
 iterReverse = do
   line - headI
   return [L.reverse line]

But the code you have above should also work, so it all depends on
what you are trying to achieve.

David

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe