4

I have two files: gadt1.ml and gadt2.ml and the second depends on the first.

gadt1.ml:

type never
type _ t1 = A1 : never  t1 | B1 : bool t1
type _ t2 = A2 : string t2 | B2 : bool t2
let get1 : bool t1 -> bool = function B1 -> true
let get2 : bool t2 -> bool = function B2 -> true

gadt2.ml:

let get1 : bool Gadt1.t1 -> bool = function Gadt.B1 -> true
let get2 : bool Gadt1.t2 -> bool = function Gadt.B2 -> true

when I compile using ocaml 4.02.3 (ocamlbuild gadt2.native), I get a warning 8 about the function Gadt2.get1 not being exhaustive. I am quite puzzled that Gadt2.get1 raises a warning while Gadt1.get1 and Gadt2.get2 don't.

My assumption was that the empty type never cannot be equal to bool so Gadt2.get1 should not raise a warning. On the other hand, if I call Gadt2.get1 with argument A1, I get a type error (as desired). Is the warning expected behaviour or a bug? What did I miss?

By the way, adding -principal to the compile flags does not change anything.

Abdallah
  • 335
  • 1
  • 7
  • 2
    I suspect it is a bug. You should ask the question at caml-list mailing list. Who is in charge of OCaml GADT is not watching SO. – camlspotter Oct 13 '15 at 04:26

3 Answers3

5

Gadt2 can only see the interface of Gadt1, not its implementation. The interface looks something like this:

type never
type _ t1 = A1 : never  t1 | B1 : bool t1
type _ t2 = A2 : string t2 | B2 : bool t2
val get1 : bool t1 -> bool
val get2 : bool t2 -> bool

Note that type never is abstract -- there's nothing to stop an implementation giving it a RHS. In particular, you could, inside gadt1.ml, write type never = bool, at which point it becomes reasonable to pass A1 to get1, and so get1 needs to be prepared for that possibility.

By contrast, string is a non-abstract type: it has a known representation, so it can't possibly be equal to bool, and so A2 could never be passed to get2.

What you seem to be trying to do with never is declare a type which is not abstract but empty, exposing its representation as having no constructors at all. Unfortunately that just isn't well-supported by OCaml; the ability to define a type that locally the compiler can tell is empty is a bit of a quirk, and not really mentioned in the manual. There's no way to express "this type is empty" in a module signature.

update: my belief is that this has since changed, and you can now write:

type never = |

to indicate that never really is an empty type, and not an abstract one.

Ben Millwood
  • 6,754
  • 24
  • 45
2

As Ben says, the problem is that in a signature type t is abstract rather than empty. One alternative is to define a type that is uninhabited:

module One = struct
  type never = { impossible : 'a . 'a }
  type _ t1 = A1 : never  t1 | B1 : bool t1
  type _ t2 = A2 : string t2 | B2 : bool t2
  let get1 : bool t1 -> bool = function B1 -> true
  let get2 : bool t2 -> bool = function B2 -> true
end

module Two = struct
  let get1 : bool One.t1 -> bool = function One.B1 -> true
  let get2 : bool One.t2 -> bool = function One.B2 -> true
end

This doesn't give any exhaustiveness warnings.

gsg
  • 9,167
  • 1
  • 21
  • 23
2

I've run into the same problem today and spent quite a lot of time looking for a solution. As Ben and gsg said, it is indeed due to the fact that the type never is abstract. The most elegant solution in my opinion is to define it as a private variant.

type never = private Never

This solves the exhaustiveness warning and makes it clear you removed the possibility of creating a variable of this type.

tsnobip
  • 289
  • 2
  • 8