Extend mutually recursive modules

Hi,

I’m trying to “extend” a set of recursive modules by adding functions to them
while keeping equalities between types. With the code below, the compiler gives me this error:

15 |   type t = Orig.X.t = Y of Y.t | Z
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This variant or record definition does not match that of type Orig.X.t
       Constructors do not match:
         Y of Orig.Y.t
       is not compatible with:
         Y of Y.t
       The types are not equal.
module Orig = struct
  module rec X : sig
    type t = Y of Y.t | Z
  end =
    X

  and Y : sig
    type item = A | B
    type t = item list
  end =
    Y
end

module rec X : sig
  type t = Orig.X.t = Y of Y.t | Z
end = struct
  type t = Orig.X.t = Y of Y.t | Z
end

and Y : sig
  type item = Orig.Y.item = A | B
  type t = item list
end = struct
  type item = Orig.Y.item = A | B
  type t = item list
end

The error disappear if don’t use recursive modules. Of course, in my real code, the types really are mutually recursive.
Is this a limitation due to recursive modules ?

module Y : sig
  type item = Orig.Y.item = A | B
  type t = item list
end = struct
  type item = Orig.Y.item = A | B
  type t = item list
end

module X : sig
  type t = Orig.X.t = Y of Y.t | Z
end = struct
  type t = Orig.X.t = Y of Y.t | Z
end

The reason I have such code is because I want to apply a deriving PPX only while testing, to avoid having a dependency on a PPX.
I’m trying to use a combination of three PPXes: ppx_gen_rec, ppx_import and ppx_deriving.show.
The code above is the output of them, my code looks like this:

module%gen rec X : sig
  type t = [%import: Orig.X.t] [@@deriving show]
end =
  X

and Y : sig
  type item = [%import: Orig.Y.item] [@@deriving show]
  type t = [%import: Orig.Y.t] [@@deriving show]
end =
  Y

Suggestions about this problem are welcome too :slight_smile:

The typechecker does not expand type definition of the form X.t while the typing of X is still in progress.Typically, your example can be reduced to

type u = A of int
module rec X: sig
  type l = int
  type t = u = A of X.l
end = X
Error: This variant or record definition does not match that of type s
     Constructors do not match:
        A of int
      is not compatible with:
        A of X.u
      The types are not equal.

The solution here is to avoid going through the self-referential path X.u:

module rec X: sig
  type l = int
  type t = u = A of l
end = X

Concerning your issue, a first question is do you need the extension to be recursive once the initial modules have been defined? Or could you use:

module X_extension = struct
  type t = Orig.X.t = Y of Orig.Y.t | Z
end
module Y_extension = struct
  type item = Orig.Y.item = A | B
end

If the extension itself is recursive, you should try to separate the initial recursive definitions from the new ones with

module rec X_recursive_extension : sig
  type t = Orig.X.t = Y of Orign.Y.t | Z
end = ...

where the definition of t makes it clear that it is still the same module being defined.

1 Like

Thanks for your reply !
I didn’t expect the typechecker to have this behavior, it’s nice to know about it :slight_smile:

Concerning my issue, the code is generated by ppx_import. Tuning it (with [@with]) would be quite hard to maintain and will not work well with [@@deriving show] that I’m trying to add on top.
The whole idea was a bit crazy, I’ll try something else.