Here's a type-checking problem I ran into today. I had a module with a variant type matching a signature that exposed the variant type.

  1. module type S = sig
  2. type t = A | B
  3. end
  4.  
  5. module M : S = struct
  6. type t = A | B
  7. end

I wanted to extend the module with some new functions, and match a new signature that extended the original signature. Easy, right?

  1. module type S' = sig
  2. include S
  3. val f : t -> t
  4. end
  5.  
  6. module M' : S' = struct
  7. include M
  8. let f = function A -> B | B -> A
  9. end

It was important to me to be able to include S in the new signature and include M in the new module to avoid duplicating code.

Then I hit a snag. As the code above stands, the two types, M.t and M'.t are different. We have a large codebase here at Jane Street, and there was some existing code that used the old module, M, and some other code that would use the new module M. I don't want to change all of our code to use the new module, and I want our code to be able to interoperate -- I don't want two different types floating around.

Simple, right? Just use with type. That is, define S' as follows.

  1. module type S' = sig
  2. include S with type t = M.t
  3. val f : t -> t
  4. end

Unfortunately, that gives the following error.

  1. In this `with' constraint, the new definition of t does not match its original definition in the constrained signature:
  2. Type declarations do not match:
  3. type t = M.t
  4. is not included in
  5. type t = A | B

The with type would all work if we hadn't exposed the variant in the original signature (check for yourself and see). But that's not viable -- I wanted to expose the variant.

I talked with some people here and we came up with a workaround, but I'd like to know if someone has a better one. Here's our workaround.

  1. module M = struct
  2. type t = A | B
  3. end
  4.  
  5. module type S = sig
  6. type t = M.t = A | B
  7. end
  8.  
  9. module type S' = sig
  10. include S val f : t -> t
  11. end
  12.  
  13. module M' : S' = struct
  14. include M let f = function A -> B | B -> A
  15. end