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`
* Using `fmap (fmapL fst)` to fix the `Left` return value of `decodeGetMany` to match what you wanted

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

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.

On 01/18/2014 03:56 AM, Nicolas Trangez wrote:
All,

I've been trying to use pipes to tackle a problem recently, but I'm a bit stuck.

> {-# LANGUAGE NoMonomorphismRestriction #-}

> import Pipes
> import qualified Pipes.Prelude as P
> import Pipes.Parse (input)
> import Pipes.Binary (DecodingError, Get, decodeGet, decodeGetMany)
> import Data.ByteString (ByteString)
> import Control.Monad.Trans.State.Strict (StateT, runStateT)

Here's the deal: I have an input consisting of a stream of bytes (or `ByteString`s, more exactly):

> myInput :: Monad m => Producer' ByteString m ()
> myInput = undefined

This input is actually a `Header`, followed by zero-or-more `Packet`s. Both have a `Get a` (as found in the `binary` package):

> data Header = Header deriving Show
> getHeader :: Get Header
> getHeader = return Header

> data Packet = Packet deriving Show
> getPacket :: Get Packet
> getPacket = return Packet

I'd like to create a function which somehow performs this parsing. It looks like it's common to use `StateT` to provide a producer to a parsing thing, so here's +- the type I expected to write:

> type ParseResult a = Either DecodingError a
> type Packets m r = Producer Packet m (ParseResult r)

> parseStream :: StateT (Producer ByteString m r) m (ParseResult (Header, Packets m r))

Finally, the code I came up with (which doesn't type-check with the above, and drives me crazy while trying to interpret the inferred type):

> parseStream = do
>     res <- decodeGet getHeader
>     case res of
>         Left e -> return $ Left e
>         Right (_, h) ->
> return $ Right (h, decodeGetMany getPacket input >-> P.map snd)

Here's what GHC infers for the above:

      parseStream :: forall (m :: * -> *) r a' a r1 (m1 :: * -> *).
                     (Monad m, Monad m1) =>
                     StateT
                       (Producer ByteString m r)
                       m
                       (Either
                          DecodingError
                          (Header,
                           Proxy
                             a'
                             a
                             ()
                             Packet
                             (StateT (Producer ByteString m1 r1) m1)
                             (Either
                                (DecodingError,
Producer ByteString (StateT (Producer ByteString m1 r1) m1) r1)
                                r1)))

This doesn't feel like the above is what I'm trying to achieve.

Any pointers how to handle this situation correctly? I guess some fidling with State* and ErrorT and whatnot would be required, but after a fair number of attempts I can't get things aligned correctly.

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

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