
This looks very similar to some code that I was working on a few months ago, https://github.com/JohnLato/lifted-lens. I never really started to use it, but everything that's there works (sadly I don't have any examples right now, but the module Language.Lens.Lifted is the top-level, and I could add an example if you're interested). First, consider how something might work without using GADTs. You'd want your server to read the identifiers, figure out the types to use for everything, and instantiate its argument at the correct types. This means you'd have a function like:
runServer :: (forall call req res. (RPC call, Binary req, Binary res) => call req res -> req -> IO res) -> IO ()
Now, I'm not entirely sure how this will interact with GADTs as you're
using them. The problem I had with lifted-lens was convincing GHC that
various constraints (that are required by certain GADT constructors) were
satisfiable at the point the constructor would be applied. I ended up
needing to do a lot of CPS-like transforms in more places than I expected.
On Thu, Jan 2, 2014 at 12:30 PM, Nicolas Trangez
Hi,
While working on the design of an RPC library (for an existing protocol), I got somewhat stuck. The system is fairly simple: for some call, a client first sends an identifier of the call, followed by a serialized form of the argument. Then the server returns some serialized result. A server exposes several procedures, all taking a certain argument type and returning a certain result type.
Below is some code which sketches my current approach. The 'client' side seems straight-forward and working (hence 'runCall'), but I didn't manage to implement the server side as I imagine it to be (i.e. the parts commented out).
Any pointers would be appreciated.
Thanks,
Nicolas
{-# LANGUAGE GADTs, RankNTypes, OverloadedStrings, KindSignatures, ScopedTypeVariables #-}
module RPC where
import Data.Word (Word32) import Data.Binary (Binary, decode, encode)
class RPC (a :: * -> * -> *) where rpcProcedureId :: a req res -> Word32 {- rpcProcedure :: Word32 -> Maybe (a req res) -}
data Service req res where Ping :: Service () () Add :: Service (Word32, Word32) Word32
instance RPC Service where rpcProcedureId p = case p of Ping -> 1 Add -> 2 {- rpcProcedure i = case i of 1 -> Just Ping 2 -> Just Add _ -> Nothing -}
runCall :: forall call req res. (RPC call, Binary req, Binary res) => call req res -> req -> IO res runCall call req = do let bs = encode req idx = rpcProcedureId call -- Send idx & bs to network, read stuff from network and interpret s <- return $ encode (3 :: Word32)
return $ decode s
runServer :: (RPC call, Binary req, Binary res) => (call req res -> req -> IO res) -> IO () {- runServer handler = do i <- return 2 -- Read from network case rpcProcedure i of Nothing -> error "No such procedure" Just (call :: call req res) -> do -- Read request from network s <- return $ encode (1 :: Word32, 2 :: Word32) let (req :: req) = decode s (res :: res) <- handler call req -- Send reply to network let res' = encode res return () -} runServer handler = undefined
main :: IO () main = do runCall Ping () >>= print runCall Add (1, 2) >>= print {- runServer handler where handler :: Service req res -> req -> IO res handler c (r :: req) = case c of Ping -> return () Add -> case r of (a, b) -> return (a + b) -}
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe