Search code examples
moduleconstraintsocamlreason

Can I implement multiple module types with 1 module?


Given these module types:

module type CodecTypes = {
  type t;
  type token;
};

module type Decode = {
  include CodecTypes;
  let decode: BsGenericParser.Parse.parser(token, t);
};

module type Encode = {
  include CodecTypes;
  let encode: t => list(token);
};

Is there a way to share the abstract types t and token between the two module types? I tried:

module type Codec = {
  include Encode;
  include Decode;
}

but the compiler complains about name clashes.


Solution

  • Indeed you can, using signature constraints with destructive substitution:

    module type Codec = {
      include Encode;
      include Decode with type t := t and type token := token;
    }
    

    Note that you can also write signature constraints with = instead of:=, but this will yield the same error. The difference between them is that = is a type equality that require the type on both sides to be equal, and := will replace ("destructively substitute") the type on the left-hand side. In this case that means Decode.t is replaced by Encode.t.

    To better illustrate this, consider this example:

    module type Codec = {
      type u;
      include Encode with type t := u;
      include Decode with type t := u and type token := token;
    }
    

    which will result in the following module type:

    module type Codec = {
      type u;
      type token;
      let encode: u => list(token);
      let decode: list(token) => u;
    };
    

    where t no longer occurs at all, having been replaced by u instead.