Type error with extensible variant type with a type parameter

Hi !
I am trying to use ocaml-vdom with eliom for web development, and I encounter a “basic” type error I cannot figure out how to fix.
I tried this example on a basic eliom template, with these (client side) declarations :

...
type 'msg Vdom.Cmd.t +=
  | Service of { on_ok: (string -> 'msg); on_error: (string -> 'msg) }

let create_service ~on_ok ~on_error = Service { on_ok; on_error }
...

Which yields following error:

File "vdom0.eliom", line 63, characters 48-53:
63 | let create_service ~on_ok ~on_error = Service { on_ok; on_error }
                                                     ^^^^^
Error: The record field on_ok belongs to the type 'a Service
       but is mixed here with fields of type 'b Service
       The type constructor Service would escape its scope

I then guessed the compiler did not figure out that on_ok and on_error had the same type, so I tried with

let create_service ~(on_ok : (string -> 'msg)) ~(on_error : (string -> 'msg)) = Service { on_ok; on_error }

But it gave the exact same error back.
As I am not very familiar with extensible variant types, can someone give me a clue of what is going on and why it is not working here ?

In case it is useful:
Here is how 'msg Vdom.Cmd.t is declared :

type 'msg t = ..
type t += Echo of 'msg | Batch of 'msg t list | Map : ('a -> 'msg) * 'a t -> 'msg t

I use ocaml-base-compiler.4.10.0, gen_js_api.1.0.6, ocaml-vdom.0.2. Here is the compilation command that yields reported error:

js_of_eliom -ppx -no-check-prims -jsopt +gen_js_api/ojs_runtime.js -c -package lwt_ppx -package js_of_ocaml-ppx -package js_of_ocaml-ppx_deriving_json -package ocaml-vdom  vdom0.eliom

Thank you for your help,
Have a good day !

This is a bug in the compiler that will be fixed in 4.12.0 and 4.11.2 once they are released (see #10010 for a detailed explanation).

One workaround is to separate the definition of the extension constructor from its first use with a dummy type definition:

type 'msg Vdom.Cmd.t +=
  | Service of { on_ok: (string -> 'msg); on_error: (string -> 'msg) }
type workaround = |
let create_service ~on_ok ~on_error = Service { on_ok; on_error }
3 Likes

What an outstanding answer, thanks a lot @octachron ! I confirm the workaround works on my side.