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

Reply via email to