A module signature constrains a module in a similar way to how an interface can constrain a class in object-oriented programming. A module signature can require that a module implements certain types and bindings and can also be used to hide implementation details. Say that we had a module called Foo defined in Foo.re. Its signature can be defined in Foo.rei. Any type or binding listed in a module's signature is exposed to other modules. Any type or binding listed in a module is hidden if a module signature exists and that type or binding isn't present in the module signature. Given a binding let foo = "foo"; in Foo.re, that binding can be both required and exposed by its module signature by including let foo: string; in Foo.rei:
/* Foo.re */
let foo = "foo";
/* Foo.rei */
let foo: string;
/* Bar.re */
Js.log(Foo.foo);
Here, Foo.rei requires...