
By now I've written a couple of implementations of (reply / response) protocols in Haskell. They all basically follow the same pattern: 1. One message type with a constructor for each, e.g. data Message = PingMsg | ... 2. One reply type with a constructor for each, e.g. data Reply = PingReply | ... 3. Any message or reply containing data has a constructor taking an argument of a type for that data, e.g. data Message = ... | SendDataMsg SendDataD | ... data SendDataD = SendDataD ByteString 4. The raw data is received from somewhere and passed through a parser written using attoparsec. 5. The messages are rendered into ByteString using ByteString.Builder. So, in essence something like this: +------+ +----------+ +-------+ +------------+ +------+ | read | --> | parse | --> | stuff | --> | render | --> | send | | raw | | into | +-------+ | to | | raw | +------+ | Messages | | ByteString | +------+ +----------+ +------------+ I'm curious to hear about other ways to handle protocols. Are there any papers written on implementing protocols using FP in particular? The protocols I've faced are rather simple things, what about more complicated ones, e.g. multi-layered protocols (ala IP - TCP)? /M -- Magnus Therning OpenPGP: 0xAB4DFBA4 email: magnus@therning.org jabber: magnus@therning.org twitter: magthe http://therning.org/magnus In order to understand recursion you must first understand recursion.

Right, what you have there is the simplest implementation and it seems pretty common to me, for RPC-style services. The only adjustments I like to make is to ensure input and return are matched up. Another way I've taken to is e.g. in Fay: data Returns a = Returns data Command = Procedure Int Char (Returns (Text,Maybe [Int])) call :: (Returns a -> Command) -> IO a call = … produce :: Returns a -> IO a -> IO a produce _ m = m handle :: Command -> IO () handle (Procedure i c r) = produce r (procedure i c) Another way I've taken was in e.g. ghc-server was to use a GADT: data Command a where LoadTarget :: Text -> Command (Producer Msg (SuccessFlag,Integer)) Eval :: Text -> Command (Duplex Text Text EvalResult) Ping :: Integer -> Command (Returns Integer) TypeOf :: Text -> Command (Returns Text) LocationAt :: FilePath -> Text -> Int -> Int -> Int -> Int -> Command (Returns SrcSpan) TypeAt :: FilePath -> Text -> Int -> Int -> Int -> Int -> Command (Returns Text) UsesAt :: FilePath -> Text -> Int -> Int -> Int -> Int -> Command (Returns Text) KindOf :: Text -> Command (Returns Text) InfoOf :: Text -> Command (Returns [Text]) Set :: Text -> Command (Returns ()) PackageConf :: FilePath -> Command (Returns ()) SetCurrentDir :: FilePath -> Command (Returns ()) My type looks like a pipe or a conduit, but was more involved because it was a duplex: type Duplex i o r = DuplexT IO i o r type Producer o r = Duplex () o r type Returns r = Duplex () () r type Unit = Duplex () () () But in essence the commands could return a result and/or accept incoming input and produce output. E.g. evaluating a line of Haskell lets you send/receive stdin/stdout and eventually return a success/fail result. I wrote a few lines of TH to do the dispatching code. I've also seen an approach using type-level literals to put strings in the types to use dispatching, but I can't remember who wrote it. You can also use e.g. closed type families to express a finite state machine that this-must-happen-before-that-state etc. if you really want to ensure every state change is valid. It's hard to find links to any of these things I've seen, not sure what the keywords are.

El Mar 5, 2015, a las 5:58, Christopher Done
Right, what you have there is the simplest implementation and it seems pretty common to me, for RPC-style services.
The only adjustments I like to make is to ensure input and return are matched up.
Another way I've taken to is e.g. in Fay:
data Returns a = Returns data Command = Procedure Int Char (Returns (Text,Maybe [Int]))
call :: (Returns a -> Command) -> IO a call = …
produce :: Returns a -> IO a -> IO a produce _ m = m
handle :: Command -> IO () handle (Procedure i c r) = produce r (procedure i c)
Another way I've taken was in e.g. ghc-server was to use a GADT:
data Command a where LoadTarget :: Text -> Command (Producer Msg (SuccessFlag,Integer)) Eval :: Text -> Command (Duplex Text Text EvalResult) Ping :: Integer -> Command (Returns Integer) TypeOf :: Text -> Command (Returns Text) LocationAt :: FilePath -> Text -> Int -> Int -> Int -> Int -> Command (Returns SrcSpan) TypeAt :: FilePath -> Text -> Int -> Int -> Int -> Int -> Command (Returns Text) UsesAt :: FilePath -> Text -> Int -> Int -> Int -> Int -> Command (Returns Text) KindOf :: Text -> Command (Returns Text) InfoOf :: Text -> Command (Returns [Text]) Set :: Text -> Command (Returns ()) PackageConf :: FilePath -> Command (Returns ()) SetCurrentDir :: FilePath -> Command (Returns ())
My type looks like a pipe or a conduit, but was more involved because it was a duplex:
type Duplex i o r = DuplexT IO i o r type Producer o r = Duplex () o r type Returns r = Duplex () () r type Unit = Duplex () () ()
But in essence the commands could return a result and/or accept incoming input and produce output. E.g. evaluating a line of Haskell lets you send/receive stdin/stdout and eventually return a success/fail result.
I wrote a few lines of TH to do the dispatching code.
I've also seen an approach using type-level literals to put strings in the types to use dispatching, but I can't remember who wrote it.
You can also use e.g. closed type families to express a finite state machine that this-must-happen-before-that-state etc. if you really want to ensure every state change is valid.
It's hard to find links to any of these things I've seen, not sure what the keywords are.
The search term for this is "session types" -- iirc there's a nice example in spj's paper "fun with type families" Tom
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Yeah, session types are worth looking at for specifying the protocol in the
type system. A good paper to look at for implementing session types in
Haskell is "Haskell Session Types with (Almost) No Class" which gives a
nice overview of the idea and presents a design that looks reasonably nice
to use.
On Thu, Mar 5, 2015 at 6:23 AM,
El Mar 5, 2015, a las 5:58, Christopher Done
escribió: Right, what you have there is the simplest implementation and it seems pretty common to me, for RPC-style services.
The only adjustments I like to make is to ensure input and return are matched up.
Another way I've taken to is e.g. in Fay:
data Returns a = Returns data Command = Procedure Int Char (Returns (Text,Maybe [Int]))
call :: (Returns a -> Command) -> IO a call = …
produce :: Returns a -> IO a -> IO a produce _ m = m
handle :: Command -> IO () handle (Procedure i c r) = produce r (procedure i c)
Another way I've taken was in e.g. ghc-server was to use a GADT:
data Command a where LoadTarget :: Text -> Command (Producer Msg (SuccessFlag,Integer)) Eval :: Text -> Command (Duplex Text Text EvalResult) Ping :: Integer -> Command (Returns Integer) TypeOf :: Text -> Command (Returns Text) LocationAt :: FilePath -> Text -> Int -> Int -> Int -> Int -> Command (Returns SrcSpan) TypeAt :: FilePath -> Text -> Int -> Int -> Int -> Int -> Command (Returns Text) UsesAt :: FilePath -> Text -> Int -> Int -> Int -> Int -> Command (Returns Text) KindOf :: Text -> Command (Returns Text) InfoOf :: Text -> Command (Returns [Text]) Set :: Text -> Command (Returns ()) PackageConf :: FilePath -> Command (Returns ()) SetCurrentDir :: FilePath -> Command (Returns ())
My type looks like a pipe or a conduit, but was more involved because it was a duplex:
type Duplex i o r = DuplexT IO i o r type Producer o r = Duplex () o r type Returns r = Duplex () () r type Unit = Duplex () () ()
But in essence the commands could return a result and/or accept incoming input and produce output. E.g. evaluating a line of Haskell lets you send/receive stdin/stdout and eventually return a success/fail result.
I wrote a few lines of TH to do the dispatching code.
I've also seen an approach using type-level literals to put strings in the types to use dispatching, but I can't remember who wrote it.
You can also use e.g. closed type families to express a finite state machine that this-must-happen-before-that-state etc. if you really want to ensure every state change is valid.
It's hard to find links to any of these things I've seen, not sure what the keywords are.
The search term for this is "session types" -- iirc there's a nice example in spj's paper "fun with type families"
Tom
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
participants (4)
-
amindfv@gmail.com
-
Christopher Done
-
Magnus Therning
-
Tikhon Jelvis