well-typed / grapesy

Native Haskell gRPC client and server based on `http2`
Other
31 stars 4 forks source link

Avoid inversion of control #153

Open edsko opened 3 weeks ago

edsko commented 3 weeks ago

This is the second of two PRs that make some (backwards compatibility breaking) API changes that hopefully will improve the usability of the library. (The other one is #148.) In the library as it is prior to this PR, there is an inversion of control between the client and the server: in the server, the server is in control when messages are send or received, but in the client the library is in control, and the client must provide callbacks that are invoked for each message to be sent and each message received. This is a bit weird, led to various irregularities in the library, and also makes client code awkward. We didn't change this until now because I was unable to find a good formulation of the types of server and client handlers that both avoided this inversion of control and still made the duality between the server and client very explicit. However, I have now finally found such a formulation:

type Send a = NextElem a -> IO ()
type Recv a = IO (NextElem a)

type    Positive m a b =                      a ->         m  b
newtype Negative m a b = Negative (forall r. (a -> m r) -> m (b, r))

type family Handler (r :: HandlerRole) (s :: StreamingType) m (rpc :: k) where
  Handler Server NonStreaming    m rpc = Input rpc -> m (Output rpc)
  Handler Client NonStreaming    m rpc = Input rpc -> m (Output rpc)

  Handler Server ClientStreaming m rpc = Positive m (Recv (Input rpc)) (Output rpc)
  Handler Client ClientStreaming m rpc = Negative m (Send (Input rpc)) (Output rpc)

  Handler Server ServerStreaming m rpc = Input rpc -> Positive m (Send (Output rpc)) ()
  Handler Client ServerStreaming m rpc = Input rpc -> Negative m (Recv (Output rpc)) ()

  Handler Server BiDiStreaming   m rpc = Positive m (Recv (Input rpc), Send (Output rpc)) ()
  Handler Client BiDiStreaming   m rpc = Negative m (Send (Input rpc), Recv (Output rpc)) ()

With this problem finally solved, we can now smooth out some of the last wrinkles in the library's API design.

edsko commented 3 days ago

Verified that the interop tests still pass.