Ok, so I have a network connection that has an interactive handshake to 
setup a connection (i.e. I need to write things to the network while 
parsing the handshake). Several months ago I wrote some code that manually 
plumbed a ByteString state throughout all my pipes to handle leftovers. 
Today I wanted to try and rewrite this code to use the new pipes-parse 
stuff, thinking the result would be much nicer to work with. However, I get 
stuck with no clue how to continue. My original code was as follows:


readSocket :: MonadIO m => Handle -> Producer ByteString m r
writeSocket :: MonadIO m => Handle -> Consumer ByteString m r

handShake :: SockAddr ->
    StateT ByteString
           (Pipe ByteString
                 ByteString
                 (ReaderT SockConfig (SafeT IO)))
           ( Pipe ByteString Frame (SafeT IO) r,
             Pipe Frame ByteString (SafeT IO) r )
handShake addr =  do
    version <- zmtpVersion
    let (decoder, encoder) = case version of
            ZMTP1 {} -> (zmtp1Decoder, zmtp1Encoder)
                :: MonadSafe m => (Pipe ByteString Frame m r, Pipe Frame 
ByteString m r)
            ZMTP2 {} -> (zmtp2Decoder, zmtp2Encoder)
            ZMTP3 {} -> (zmtp3Decoder, zmtp3Encoder)

    -- authenticate version addr :: Pipe Frame Frame m r
    lift $ decoder >-> authenticate version addr >-> encoder
    return (decoder, encoder)

session :: SockConfig
        -> Producer Frame (SafeT IO) ()
        -> Consumer Frame (SafeT IO) ()
        -> IO Handle
        -> SockAddr
        -> IO ()
session cfg fromApp toApp newHandle addr =
  runSafeT . runEffect . bracket newHandle hClose $ \hnd -> do

      let fromNetwork = readSocket hnd
          toNetwork = writeSocket hnd
          handshake = runReaderP cfg (runStateT (handShake addr) empty)

      ((decoder, encoder), rest) <- fromNetwork >-> handshake >-> toNetwork
      myTid <- liftIO myThreadId

      let appToNet = fromApp >-> encoder >-> toNetwork
          netToApp = (yield rest >> fromNetwork) >-> decoder >-> toApp
          -- code using appToNet/netToApp Effect's

In other words, my handshake is just a pipe, reading and writing 
ByteStrings from and to the same socket. After version negotiation I do 
authentication, this uses a "Pipe Frame Frame" that I compose with the 
relevant decoders/encoders and lift into the handshake's pipe. So far, so 
good, the only real nastiness is manually threading the leftover state 
throughout the zmtpVersion, decoder and encoder.

So on to my attempt to rewrite this using pipes-parse.

readSocket :: MonadIO m => Handle -> Producer ByteString m r
writeSocket :: MonadIO m => Handle -> Consumer ByteString m r

handShake :: SockAddr
          -> Parser ByteString (Producer ByteString (ReaderT SockConfig 
(SafeT IO))) (Get Frame, Frame -> Put)
handShake _addr =  do
    version <- zmtpVersion
    let (getFrame, putFrame) = case version of
            ZMTP1 {} -> (ZMTP1.getFrame, ZMTP1.putFrame)
            ZMTP2 {} -> (ZMTP2.getFrame, ZMTP2.putFrame)
            ZMTP3 {} -> (ZMTP3.getFrame, ZMTP3.putFrame)

    --lift $ undefined >-> authenticate version addr >-> undefined
    return (getFrame, putFrame)

session :: SockConfig
        -> Producer Frame (SafeT IO) ()
        -> Consumer Frame (SafeT IO) ()
        -> IO Handle
        -> SockAddr
        -> IO ()
session cfg fromApp toApp newHandle addr =
  runSafeT . runEffect . bracket newHandle hClose $ \hnd -> do

      let fromNetwork = readSocket hnd
          toNetwork = writeSocket hnd
          handshake = runReaderP cfg . runStateT (handShake addr)

      ((decoder, encoder), rest) <- handshake fromNetwork >-> toNetwork
      myTid <- liftIO myThreadId

      let appToNet = for fromApp (encodePut . encoder) >-> toNetwork

I immediately run into several problems. First off, my authentication code 
wants to deal with Frame's, not ByteString, but my initial has a ByteString 
producer. I can see that lenses are supposed to let me "zoom" "Parser a" to 
"Parser b", but unfortunately pipes-binary only has the following lens:

decoded :: (Monad m, Binary a) => Lens' (Producer ByteString m r) (Producer a 
m (Either (DecodingError, Producer ByteString m r) r))

This only works with Binary instances, unfortunate in my case where I need 
to use explicit getters/putters, but writing my own decoder that work using 
decodeGet and encodePut is not too hard. A bigger issue is, how to handle 
"authenticate", since it wants to deal with Frame's it would ideally have 
the type "Parser Frame (Producer Frame (ReaderT SockConfig (SafeT IO)) r", 
but I have no way to run this within my "Parser ByteString (Producer 
ByteString (ReaderT SockConfig (SafeT IO))" as the types don't match.

Even worse, after the "runState (handShake addr)" in session the remainder 
of fromNetwork's output is in the Producer returned as "rest", 
unfortunately the trip through the Parser's StateT has changed 
fromNetwork's type to "Producer ByteString (Producer ByteString (ReaderT 
SockConfig (SafeT IO))", I can't compose this with session "Consumer Frame 
(SafeT IO) ()", because the inner Producer and ReaderT are in the way, even 
though these are now useless as only the outer Producer will still do 
something. I could get rid of the ReaderT with a "runReaderP undefined", 
but I have no way to get rid of the inner Producer...

Any suggestions on how to do this in the new pipes-parse world? Or should I 
just give up and stick with my old design of explicit passing around of 
leftovers?

Cheers,
Merijn

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