Thank you, Gabriel, for the thorough answer. On Sat, 2014-01-18 at 07:11 +0700, Gabriel Gonzalez wrote: > Here is the code that type-checks: > > import Control.Error (fmapL) > import Control.Monad (void) > import Control.Monad.Trans.State.Strict (StateT, get) > import Data.ByteString (ByteString) > import Data.Binary.Get (Get) > import Pipes > import qualified Pipes.Prelude as P > import Pipes.Binary > > data Header > data Packet > > getHeader :: Get Header > getHeader = undefined > > getPacket :: Get Packet > getPacket = undefined > > type ParseResult = Either DecodingError > > type Packets m r = Producer Packet m (ParseResult r) > > parseStream > :: Monad m > => StateT (Producer ByteString m r) m (ParseResult (Header, > Packets m r)) > parseStream = do > res <- decodeGet getHeader > case res of > Left e -> return (Left e) > Right (_, h) -> do > p <- get > return $ Right > ( h > , fmap (fmapL fst) $ decodeGetMany getPacket p >-> > P.map snd > ) > > The main changes I made to your code were: > > * Using `get` to retrieve the remaining input instead of `input`
I experimented using `get` before, but that didn't work out. > * Using `fmap (fmapL fst)` to fix the `Left` return value of > `decodeGetMany` to match what you wanted Interesting. I should get used to the 'double fmap' paradigm. > More generally, `input` is getting phased out for the next major release > of `pipes-parse`. See this thread for more details: > > https://groups.google.com/forum/?fromgroups#!topic/haskell-pipes/-fsWSm26l6U Thanks for the pointer. Those new interfaces look useful. > The code I wrote would remain mostly unchanged after the `pipes-parse` > update, but it would be less idiomatic. The main things that would > change is that you would simplify the type using the `Parser` type > synonym to: > > parseStream > :: Monad m > => Parse ByteString m (ParseResult (Header, Packets m r)) > > ... and you would use `p ^. decodedL getPacket` instead of > `decodeGetMany getPacket p`. > > However, the more idiomatic thing to do (both before and after the > update) would be to define the following function: > > parseStream > :: Monad m > => Producer ByteString m r > -> m (ParseResult (Header, Producer Packet m (ParseResult r))) > parseStream p = do > (res, p') <- runStateT (decodeGet getHeader) p > case res of > Left e -> return (Left e) > Right (_, h) -> do > return $ Right > ( h > , fmap (fmapL fst) $ decodeGetMany getPacket p' >-> > P.map snd > ) > > The reason why is that you don't want to keep the `ByteString` leftovers > around because they are all getting converted to `Packet`s. The only > difference is that I've removed the `StateT` layer that would > unnecessarily hold onto the `ByteString` leftovers. Yes indeed. I was puzzled by the use of `StateT` for `decodeGet`, but passing a `Producer` as input in `decodeGetMany`. I should've given this some more thought, because it makes perfect sense (I got the insight thanks to your message), and indeed, the approach you take above is what I need (maybe not getting rid of the leftovers though, I'll see). Thanks! Nicolas -- 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 [email protected]. To post to this group, send email to [email protected].
