{"action":"created","issue":{"url":"https://api.github.com/repos/k-bx/protocol-buffers/issues/21","labels_url":"https://api.github.com/repos/k-bx/protocol-buffers/issues/21/labels{/name}","comments_url":"https://api.github.com/repos/k-bx/protocol-buffers/issues/21/comments","events_url":"https://api.github.com/repos/k-bx/protocol-buffers/issues/21/events","html_url":"https://github.com/k-bx/protocol-buffers/pull/21","id":125972704,"number":21,"title":"Generate code for services","user":{"login":"alexbiehl","id":1876617,"avatar_url":"https://avatars.githubusercontent.com/u/1876617?v=3","gravatar_id":"","url":"https://api.github.com/users/alexbiehl","html_url":"https://github.com/alexbiehl","followers_url":"https://api.github.com/users/alexbiehl/followers","following_url":"https://api.github.com/users/alexbiehl/following{/other_user}","gists_url":"https://api.github.com/users/alexbiehl/gists{/gist_id}","starred_url":"https://api.github.com/users/alexbiehl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexbiehl/subscriptions","organizations_url":"https://api.github.com/users/alexbiehl/orgs","repos_url":"https://api.github.com/users/alexbiehl/repos","events_url":"https://api.github.com/users/alexbiehl/events{/privacy}","received_events_url":"https://api.github.com/users/alexbiehl/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":2,"created_at":"2016-01-11T15:47:33Z","updated_at":"2016-01-11T16:12:47Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/k-bx/protocol-buffers/pulls/21","html_url":"https://github.com/k-bx/protocol-buffers/pull/21","diff_url":"https://github.com/k-bx/protocol-buffers/pull/21.diff","patch_url":"https://github.com/k-bx/protocol-buffers/pull/21.patch"},"body":"Hello, \r\n\r\nI open this pull request for discussion (started here https://github.com/k-bx/protocol-buffers/issues/9). I put client and server examples as use cases here. Please have look. \r\n\r\nHaving a proto-definition like this: \r\n```protobuf\r\nmessage SearchRequest {\r\n    required string query = 1;\r\n    optional int32 page_number = 2;\r\n    optional int32 result_per_page = 3;\r\n}\r\n\r\nmessage SearchResponse {\r\n    repeated string results = 1;\r\n}\r\n\r\nmessage AutocompleteRequest {\r\n    required string query = 1;\r\n    optional int32 max_results = 2;\r\n}\r\n\r\nmessage AutocompleteResponse {\r\n    repeated string results = 1;\r\n}\r\n\r\nservice SearchService {\r\n    rpc Search (SearchRequest) returns (SearchResponse);\r\n    rpc Autocomplete(AutocompleteRequest) returns (AutocompleteResponse);\r\n}\r\n````\r\n\r\nThe service generator generates (omitting imports, exports and corresponding messages)\r\n````haskell\r\n\r\ntype SearchService = P'.Service '[Search, Autocomplete]\r\n\r\nsearchService :: SearchService\r\nsearchService = P'.Service\r\n\r\ntype Search = P'.Method \".Search.SearchService.Search\" SearchRequest SearchResponse\r\n\r\ntype Autocomplete = P'.Method \".Search.SearchService.Autocomplete\" AutocompleteRequest AutocompleteResponse\r\n\r\nsearch :: Search\r\nsearch = P'.Method\r\n\r\nautocomplete :: Autocomplete\r\nautocomplete = P'.Method\r\n\r\n````\r\n\r\nA `Service` consists of a type level list itself consisting of `Method`s. Each `Method` is parameterized with its method name as type-level string (via DataKinds extensions), its input parameter type and its output parameter type.\r\n\r\nAn implementor for a client of a protobuf service only has to interpret the method type for his calls like so: \r\n````haskell\r\n-- invoke reifies the Method type to value level. This is the adapter one has to\r\n-- write in order use the generated method definitions.\r\ninvoke :: forall m name req resp . (MonadIO m, MethodCxt name req resp) => Method name req resp -> req -> RpcM m resp\r\ninvoke method = \\req -> ... \r\n  where name = methodName method\r\n````\r\nWhere \r\n  - `MethodCxt` is a constraint given in `Text.ProtocolBuffers.Services`\r\n  - `methodName` is a function from `Method name req resp` to String also given in `Text.ProtocolBuffers.Services`\r\n\r\nAny other constraints, like the monad in which the `invoke` operation is performed can be freely chosen by the implementor. Its important only to interpret the `Method name req resp` type.\r\n\r\nOn the server side an implementor needs to combine his `Method`s with corresponding handlers: \r\n\r\n````haskell\r\ndoSearch :: SearchRequest -> SearchM SearchResponse \r\ndoSearch req = do ... \r\n\r\ndoAutocomplete :: AutocompleteRequest -> SearchM AutocompleteResponse\r\ndoAutocomplete req = do ... \r\n\r\nhandlers :: [MethodHandler SearchM]\r\nhandlers = reifyMethods searchService [] \r\n                     doSearch\r\n                     doAutocomplete\r\n\r\n```` \r\n\r\nWe define our handler functions `doSearch` and `doAutocomplete`. We use the `reifyMethods` function from `Text.ProtocolBuffers.Services` (note the `[]` argument, hopefully someone smarter than me can get rid of it) to get a list of `MethodHandler`s.\r\n\r\n````haskell\r\n-- | A reified method attached with a function to execute the method.\r\ndata MethodHandler m = forall name req resp. MethodCxt name req resp =>\r\n                       MethodHandler (Method name req resp) (req -> m resp)\r\n````\r\n\r\nA `MethodHandler` is basically the method name and an action which takes the methods input parameter and outputs a response in some context `m`.\r\n\r\nIt follows a more elaborated example using `binary`s facilities to process messages: \r\n\r\n````haskell\r\ndata SomeHeader = SomeHeader { hdMethod    :: !ByteString\r\n                             , hdPayloadSz :: !Word32\r\n                             }\r\n\r\n-- This turns bytestrings into actions which handle the incoming service calls,\r\n-- which in turn respond with a put to put it back on the wire/file.\r\n-- Again, we are free to handle the methods as we want, no constraints given through\r\n-- the encoding of methods.\r\nhandle :: Functor m => [MethodHandler m] -> LByteString.ByteString -> Result (m Put.Put)\r\nhandle methods =\r\n  runGet (do mSz <- fromIntegral <$> getWord8\r\n             SomeHeader{..} <- SomeHeader <$> getByteString mSz <*> getWord32be\r\n             case Map.lookup hdMethod methods' of\r\n               Just (MethodHandler _ f) -> do\r\n                 req <- messageGetM\r\n                 return (messagePutM <$> f req)\r\n               Nothing -> fail \"invalid method\")\r\n  where\r\n    -- An efficient mapping from method names to their handlers\r\n    methods' = Map.fromList [ (m, mh) | mh@(MethodHandler method _) <- methods\r\n                                      , let m = ByteString.pack (methodName method) ]\r\n````\r\n\r\nThe resulting (possibly) monadic action from `handle` produces a result from the input parameter and just generates a response which is in turn directly serialized to a `Put` again. Again the implementor is free to run their actions in any context and is not constraint in his freedom of choice for Api. \r\n\r\n"},"comment":{"url":"https://api.github.com/repos/k-bx/protocol-buffers/issues/comments/170599681","html_url":"https://github.com/k-bx/protocol-buffers/pull/21#issuecomment-170599681","issue_url":"https://api.github.com/repos/k-bx/protocol-buffers/issues/21","id":170599681,"user":{"login":"alexbiehl","id":1876617,"avatar_url":"https://avatars.githubusercontent.com/u/1876617?v=3","gravatar_id":"","url":"https://api.github.com/users/alexbiehl","html_url":"https://github.com/alexbiehl","followers_url":"https://api.github.com/users/alexbiehl/followers","following_url":"https://api.github.com/users/alexbiehl/following{/other_user}","gists_url":"https://api.github.com/users/alexbiehl/gists{/gist_id}","starred_url":"https://api.github.com/users/alexbiehl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alexbiehl/subscriptions","organizations_url":"https://api.github.com/users/alexbiehl/orgs","repos_url":"https://api.github.com/users/alexbiehl/repos","events_url":"https://api.github.com/users/alexbiehl/events{/privacy}","received_events_url":"https://api.github.com/users/alexbiehl/received_events","type":"User","site_admin":false},"created_at":"2016-01-11T16:12:47Z","updated_at":"2016-01-11T16:12:47Z","body":"What makes me wonder is this http://www.grpc.io/docs/guides/wire.html \r\n\r\ngRPC is an implementation for protocol-buffer services over http2 from google. They include the pseudo-header `:path = /google.pubsub.v2.PublisherService/CreateTopic` I wonder where they took the prefix from. "}}