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 <nicolas@incubaid.com> wrote:
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