Generic Module Signatures

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.

1 Like