reasonml / reason

Simple, fast & type safe code that leverages the JavaScript & OCaml ecosystems
http://reasonml.github.io
MIT License
10.14k stars 428 forks source link

Mutually recursive modules introduces too much complexity, new syntax #2326

Closed canadaduane closed 3 months ago

canadaduane commented 5 years ago

I have a module that I realize I'd like to split into two; however, it contains a pair of mutually recursive types, and the types must be separated for the module to be separated.

Here's the module "before" (simplified for this issue):

module AB = {
  type tA =
    | Aardvark(tB)
    | Apple
  and tB =
    | Bat(tA)
    | Beer;
  let mkA = b => Aardvark(b);
  let mkB = a => Bat(a);
};

To re-organize my code into the modules I'd like, I think it might be as easy as something like this:

module A = {
  type t =
    | Aardvark(B.t)
    | Apple;
  let make = a => B.Bat(a);
};
module B = {
  type t =
    | Bat(A.t)
    | Beer;
  let make = b => A.Aardvark(b);
};

There's an error at Aardvark(B.t) because B isn't declared yet. Ok, no problem, I've already encountered mutually recursive types, so maybe I just need to make the modules mutually recursive:

module rec A = {
  type t =
    | Aardvark(B.t)
    | Apple;
  let make = a => B.Bat(a);
}
and B = {
  type t =
    | Bat(A.t)
    | Beer;
  let make = b => A.Aardvark(b);
};

That doesn't work either: Recursive modules require an explicit module type. Ok, this is the first time I've used module types (signatures?) but I'll give it a go. After a lot of trial and error, I arrive at this (working) code:

module rec A: {
  type t =
    | Aardvark(B.t)
    | Apple;
  let make: t => B.t;
} = {
  type t =
    | Aardvark(B.t)
    | Apple;
  let make = a => B.Bat(a);
}
and B: {
  type t =
    | Bat(A.t)
    | Beer;
  let make: t => A.t;
} = {
  type t =
    | Bat(A.t)
    | Beer;
  let make = b => A.Aardvark(b);
};

This is a fairly short example, but if there are more types that need to be split up in this way, the code size explodes as a result of this refactor.

In addition, there are new concepts, some new syntax, and more repetition (variants in each type t) in the result. Ideally, I'd like the language to make re-organizing code and separating into modules less burdensome a choice.

anmonteiro commented 3 months ago

outside the scope of Reason, this is about type checking in OCaml