[Haskell-cafe] Designing somewhat-type-safe RPC
Nicolas Trangez
nicolas at incubaid.com
Thu Jan 2 20:30:20 UTC 2014
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)
-}
More information about the Haskell-Cafe
mailing list