You are correct in that it is not possible to define a generic signature for the Endpoint bodies that also defines the signature for make
but the right solution depends on what you are trying to achieve.
If the bodies/implementation for the Endpoints are given already (maybe they are auto-generated), you may not need to specify a signature for the implementation. However, It can be useful to define a common module signature to allow defining helper functions to e.g. making a request. For example:
module EndpointA = struct
type t = { item_a: string; item_b: string } [@@deriving yojson_of]
let make ~item_a ~item_b () = { item_a; item_b }
end
module type Endpoint = sig
type t [@@deriving yojson_of]
end
let call_endpoint (type a) (module E : Endpoint with type t = a) (t : a) =
...
(* Assuming there is a generic function that can send the request to a specific endpoint with the signature: send_request: Yojson.Basic.t -> unit *)
let () = send_request (E.t_to_yojson t) in
...
(* Call the endpoint *)
let message = EndpointA.make ~item_a:"Hello" ~item_b:"World" () in
let _ = call_endpoint (module EndpointA) message
As the implementation of call_endpoint
does not need to call Endpoint.make
, there is no need to define it in the the Endpoint
module signature. The only drawback is that if Endpoints are not a superset of the Endpoint
signature the error location will not be at module definition point, but where its used.