Polymorphic variants with variance, interfaces and catch-all matches

Hello! I stumbled on a type error in OCaml 4.14 using polymorphic variants with [< .. ] variance in the signature of a module. In the implementation I match on the polymorphic variant in a function with a wildcard pattern and then I get a confusing type error. However, if I spell out the cases I don’t get a type error. I don’t quite understand.

Here it fails:

module M : sig
  val f : [< `Read | `Write ] -> int -> unit
end = struct
  let f mode len =
    match mode with
    | `Write -> print_endline (String.make (max 0 (pred len)) 'E')
    | _ -> ignore (input_line stdin)
end

And the type error is the following:

Error: Signature mismatch:
       Modules do not match:
         sig val f : [> `Write ] -> int -> unit end
       is not included in
         sig val f : [< `Read | `Write ] -> int -> unit end
       Values do not match:
         val f : [> `Write ] -> int -> unit
       is not included in
         val f : [< `Read | `Write ] -> int -> unit
       The type [> `Write ] -> int -> unit is not compatible with the type
         [< `Read | `Write ] -> int -> unit
       The tag `Write is guaranteed to be present in the first variant type,
       but not in the second

Changing the _ pattern to `Read the compiler is happy again.

module M : sig
  val f : [< `Read | `Write ] -> int -> unit
end = struct
  let f mode len =
    match mode with
    | `Write -> print_endline (String.make (max 0 (pred len)) 'E')
    | `Read -> ignore (input_line stdin)
end

Edited to add:
Adding a type ascription in the implementation does not change anything:

module M : sig
  val f : [< `Read | `Write ] -> int -> unit
end = struct
  let f (mode : [< `Read | `Write ]) len =
    match mode with
    | `Write -> print_endline (String.make (max 0 (pred len)) 'E')
    | _ -> ignore (input_line stdin)
end

See types - Wildcard pattern overriding subtype constraint on polymorphic variant - Stack Overflow

Cheers,
Nicolas

1 Like