Local abstract type and polymorphic variant

I was playing with the typer with some RPC description (a binding for libgrpc and ocaml-protoc-plugin) in OCaml 3.13.1:

  module type Message = sig
    type t
  end

  type 'a feature_availability = ([< `Yes | `No ] as 'a)
  (** Used to indicate that a service as some feature or not *)

  module type Rpc = sig
    module Request : Message
    type client_streaming
  end

  type ('req,'cs) service =
    (module Rpc with type Request.t = 'req and
    type client_streaming = 'cs feature_availability)

  let make_service_functions :
    type req cs . (req,([< `Yes | `No ] as cs)) service -> _ =
    fun r ->
    let module R = (val r) in
    R.Request.from_proto, R.Response.to_proto

I got the typing error:

File "ocaml-protoc-plugin/src/ocaml_protoc_plugin/service.ml", line 37, characters 43-45:
37 |     type req cs . (req,([< `Yes | `No ] as cs)) servic
                                                ^^
Error: Syntax error

Is there some way to type this function?

type req cs . ... is sugar for universal quantification + abstract type variables (pointer in the manual) so you can try to unsugar it by hand:

let make_service_functions :
    'req 'cs . ('req,([< `Yes | `No ] as 'cs)) service -> _ =
    fun (type req) (type cs) r ->
    ...

It’s even possible that you don’t even need the type variables and can remove the (type req) (type cs) part.

I couldn’t get it to work with polymorphic variants, but here is a version that seems to work, with GADTs:

module type Message = sig
  type t
end

type yes = Yes_t
type no = Not_t
type 'a feature_availability =
  | Yes : yes feature_availability
  | No : no feature_availability
  (** Used to indicate that a service as some feature or not *)

module type Rpc = sig
  module Request : Message
  type client_streaming
end

type ('req,'cs) service =
  (module Rpc with type Request.t = 'req and
  type client_streaming = 'cs feature_availability)

let make_service_functions :
  type req cs. (req, cs feature_availability) service -> _ =
  fun r ->
  let module R = (val r) in
  R.Request.from_proto, R.Response.to_proto

(this code doesn’t type-check, but for more obvious reasons: from_proto is missing from Request, and Response is missing)

The val r requires to use local abstract type:

File "ocaml-protoc-plugin/src/ocaml_protoc_plugin/service.ml", line 61, characters 19-26:
61 |     let module R = (val r) in
                        ^^^^^^^
Error: The type of this packed module contains variables:
       ('req, [< `No | `Yes ]) service

Great use of GADT, thank you! I used your solution [Service] Use a GADT for representing feature availability · bobot/ocaml-protoc-plugin@3ba81c4 · GitHub