Proposed enhancement of `module type of ` semantics

Note this is a change proposed for a major release.

Below is the motivation code:

module Foo = {
  type navigationApi = {
   one: [#1 | #2],
    two: int,
  }
  let apiImpl: navigationApi = { one: #1, two: 2}
}

module type Foo = module type of Foo 
let foo = module(Foo: Foo)

let baz: Foo.navigationApi = switch foo {
| module(Foo) => Foo.apiImpl
}

Some users wrote such code snippet and got a confusing error message:

[E] Line 13, column 17:
This has type: \"Foo/1017".navigationApi
  Somewhere wanted: \"Foo/1002".navigationApi
  The type constructor Foo/1017.navigationApi would escape its scope

The root cause is that module type of Foo is not really Foo's type, the type equivalence is lost, so user has to write this

module type Foo = module type of Foo with type navigationApi = Foo.navigationApi

If you have many types you can list it one by one, or use

module type Foo = module type of { include Foo}

IMHO, strengthen by default is a more intuitive semantics, so that in rescript we can desugar its module type of Foo to such semantics.

Desugaring in syntax level is one way of implementation, we can also implement it in the type checking directly.

2 Likes

Yes, this would be good imo. The difference between module type of Foo and module type of { include Foo } has hit me before as well, and I think most users would intuitively mean the second one when they type the first one. I am wondering though of use-cases of the first (I can’t really come up with one on the top of my head).