--- title: "Module" description: "ReScript modules, module signatures and interface files" canonical: "/docs/manual/v11.0.0/module" --- # Module ## Basics **Modules are like mini files**! They can contain type definitions, `let` bindings, nested modules, etc. ### Creation To create a module, use the `module` keyword. The module name must start with a **capital letter**. Whatever you could place in a `.res` file, you may place inside a module definition's `{}` block. ```res example module School = { type profession = Teacher | Director let person1 = Teacher let getProfession = (person) => switch person { | Teacher => "A teacher" | Director => "A director" } } ``` ```js function getProfession(person) { if (person) { return "A director"; } else { return "A teacher"; } } var School = { person1: /* Teacher */0, getProfession: getProfession }; ``` A module's contents (including types!) can be accessed much like a record's, using the `.` notation. This demonstrates modules' utility for namespacing. ```res let anotherPerson: School.profession = School.Teacher Console.log(School.getProfession(anotherPerson)) /* "A teacher" */ ``` ```js var anotherPerson = /* Teacher */0; console.log("A teacher"); ``` Nested modules work too. ```res example module MyModule = { module NestedModule = { let message = "hello" } } let message = MyModule.NestedModule.message ``` ```js var NestedModule = { message: message }; var MyModule = { NestedModule: NestedModule }; var message = MyModule.NestedModule.message; ``` ### `open`ing a module Constantly referring to a value/type in a module can be tedious. Instead, we can "open" a module and refer to its contents without always prepending them with the module's name. Instead of writing: ```res let p = School.getProfession(School.person1) ``` ```js var p = School.getProfession(School.person1); ``` We can write: ```res open School let p = getProfession(person1) ``` ```js var p = School.getProfession(School.person1); ``` The content of `School` module are made visible (**not** copied into the file, but simply made visible!) in scope. `profession`, `getProfession` and `person1` will thus correctly be found. **Use `open` this sparingly, it's convenient, but makes it hard to know where some values come from**. You should usually use `open` in a local scope: ```res let p = { open School getProfession(person1) } /* School's content isn't visible here anymore */ ``` ```js var p = School.getProfession(School.person1); ``` ### Use `open!` to ignore shadow warnings There are situations where `open` will cause a warning due to existing identifiers (bindings, types) being redefined. Use `open!` to explicitly tell the compiler that this is desired behavior. ```res let map = (arr, value) => { value } // opening Array would shadow our previously defined `map` // `open!` will explicitly turn off the automatic warning open! Array let arr = map([1,2,3], (a) => { a + 1}) ``` **Note:** Same as with `open`, don't overuse `open!` statements if not necessary. Use (sub)modules to prevent shadowing issues. ### Destructuring modules **Since 9.0.2** As an alternative to `open`ing a module, you can also destructure a module's functions and values into separate let bindings (similarly on how we'd destructure an object in JavaScript). ```res module User = { let user1 = "Anna" let user2 = "Franz" } // Destructure by name let {user1, user2} = module(User) // Destructure with different alias let {user1: anna, user2: franz} = module(User) ``` ```js var user1 = "Anna"; var user2 = "Franz"; var User = { user1: user1, user2: user2 }; ``` **Note:** You can't extract types with module destructuring — use a type alias instead (`type user = User.myUserType`). ### Extending modules Using `include` in a module statically "spreads" a module's content into a new one, thus often fulfill the role of "inheritance" or "mixin". **Note**: this is equivalent to a compiler-level copy paste. **We heavily discourage `include`**. Use it as last resort! ```res example module BaseComponent = { let defaultGreeting = "Hello" let getAudience = (~excited) => excited ? "world!" : "world" } module ActualComponent = { /* the content is copied over */ include BaseComponent /* overrides BaseComponent.defaultGreeting */ let defaultGreeting = "Hey" let render = () => defaultGreeting ++ " " ++ getAudience(~excited=true) } ``` ```js function getAudience(excited) { if (excited) { return "world!"; } else { return "world"; } } var BaseComponent = { defaultGreeting: "Hello", getAudience: getAudience }; var defaultGreeting = "Hey"; function render(param) { return "Hey world!"; } var ActualComponent = { getAudience: getAudience, defaultGreeting: defaultGreeting, render: render }; ``` **Note**: `open` and `include` are very different! The former brings a module's content into your current scope, so that you don't have to refer to a value by prefixing it with the module's name every time. The latter **copies over** the definition of a module statically, then also do an `open`. ### Every `.res` file is a module Every ReScript file is itself compiled to a module of the same name as the file name, capitalized. The file `React.res` implicitly forms a module `React`, which can be seen by other source files. **Note**: ReScript file names should, by convention, be capitalized so that their casing matches their module name. Uncapitalized file names are not invalid, but will be implicitly transformed into a capitalized module name. I.e. `file.res` will be compiled into the module `File`. To simplify and minimize the disconnect here, the convention is therefore to capitalize file names. ## Signatures A module's type is called a "signature", and can be written explicitly. If a module is like a `.res` (implementation) file, then a module's signature is like a `.resi` (interface) file. ### Creation To create a signature, use the `module type` keyword. The signature name must start with a **capital letter**. Whatever you could place in a `.resi` file, you may place inside a signature definition's `{}` block. ```res example /* Picking up previous section's example */ module type EstablishmentType = { type profession let getProfession: profession => string } ``` ```js // Empty output ``` A signature defines the list of requirements that a module must satisfy in order for that module to match the signature. Those requirements are of the form: - `let x: int` requires a `let` binding named `x`, of type `int`. - `type t = someType` requires a type field `t` to be equal to `someType`. - `type t` requires a type field `t`, but without imposing any requirements on the actual, concrete type of `t`. We'd use `t` in other entries in the signature to describe relationships, e.g. `let makePair: t => (t, t)` but we cannot, for example, assume that `t` is an `int`. This gives us great, enforced abstraction abilities. To illustrate the various kinds of type entries, consider the above signature `EstablishmentType` which requires that a module: - Declare a type named `profession`. - Must include a function that takes in a value of the type `profession` and returns a string. **Note**: Modules of the type `EstablishmentType` can contain more fields than the signature declares, just like the module `School` in the previous section (if we choose to assign it the type `EstablishmentType`. Otherwise, `School` exposes every field). This effectively makes the `person1` field an enforced implementation detail! Outsiders can't access it, since it's not present in the signature; the signature **constrained** what others can access. The type `EstablishmentType.profession` is **abstract**: it doesn't have a concrete type; it's saying "I don't care what the actual type is, but it's used as input to `getProfession`". This is useful to fit many modules under the same interface: ```res module Company: EstablishmentType = { type profession = CEO | Designer | Engineer | ... let getProfession = (person) => ... let person1 = ... let person2 = ... } ``` ```js function getProfession(person) { ... } var person1 = ... var person2 = ... var Company = { getProfession: getProfession, person1: person1, person2: person2 }; ``` It's also useful to hide the underlying type as an implementation detail others can't rely on. If you ask what the type of `Company.profession` is, instead of exposing the variant, it'll only tell you "it's `Company.profession`". ### Extending module signatures Like modules themselves, module signatures can also be extended by other module signatures using `include`. Again, **heavily discouraged**: ```res example module type BaseComponent = { let defaultGreeting: string let getAudience: (~excited: bool) => string } module type ActualComponent = { /* the BaseComponent signature is copied over */ include BaseComponent let render: unit => string } ``` ```js // Empty output ``` **Note**: `BaseComponent` is a module **type**, not an actual module itself! If you do not have a defined module type, you can extract it from an actual module using `include (module type of ActualModuleName)`. For example, we can extend the `List` module from the standard library, which does not define a module type. ```res example module type MyList = { include (module type of List) let myListFun: list<'a> => list<'a> } ``` ```js // Empty output ``` ### Every `.resi` file is a signature Similar to how a `React.res` file implicitly defines a module `React`, a file `React.resi` implicitly defines a signature for `React`. If `React.resi` isn't provided, the signature of `React.res` defaults to exposing all the fields of the module. Because they don't contain implementation files, `.resi` files are used in the ecosystem to also document the public API of their corresponding modules. ```res example /* file React.res (implementation. Compiles to module React) */ type state = int let render = (str) => str ``` ```js function render(str) { return str; } ``` ```res sig /* file React.resi (interface. Compiles to the signature of React.res) */ type state = int let render: string => string ``` ## Module Functions Modules can be passed to functions! It would be the equivalent of passing a file as a first-class item. However, modules are at a different "layer" of the language than other common concepts, so we can't pass them to *regular* functions. Instead, we pass them to special functions called module functions. The syntax for defining and using module functions is very much like the syntax for defining and using regular functions. The primary differences are: - Module functions use the `module` keyword instead of `let`. - Module functions take modules as arguments and return a module. - Module functions *require* annotating arguments. - Module functions must start with a capital letter (just like modules/signatures). Here's an example `MakeSet` module function, that takes in a module of the type `Comparable` and returns a new set that can contain such comparable items. ```res prelude module type Comparable = { type t let equal: (t, t) => bool } module MakeSet = (Item: Comparable) => { // let's use a list as our naive backing data structure type backingType = list let empty = list{} let add = (currentSet: backingType, newItem: Item.t): backingType => // if item exists if currentSet->List.some(x => Item.equal(x, newItem)) { currentSet // return the same (immutable) set (a list really) } else { list{ newItem, ...currentSet // prepend to the set and return it } } } ``` ```js var List = require("./stdlib/list.js"); function MakeSet(Item) { var add = function(currentSet, newItem) { if ( List.exists(function(x) { return Item.equal(x, newItem); }, currentSet) ) { return currentSet; } else { return { hd: newItem, tl: currentSet, }; } }; return { empty: /* [] */ 0, add: add, }; } ``` Module functions can be applied using function application syntax. In this case, we're creating a set, whose items are pairs of integers. ```res example module IntPair = { type t = (int, int) let equal = ((x1: int, y1: int), (x2, y2)) => x1 == x2 && y1 == y2 let create = (x, y) => (x, y) } /* IntPair abides by the Comparable signature required by MakeSet */ module SetOfIntPairs = MakeSet(IntPair) ``` ```js function equal(param, param$1) { if (param[0] === param$1[0]) { return param[1] === param$1[1]; } else { return false; } } function create(x, y) { return [x, y]; } var IntPair = { equal: equal, create: create, }; var SetOfIntPairs = { empty: /* [] */ 0, add: add, }; ``` ### Module functions types Like with module types, module function types also act to constrain and hide what we may assume about module functions. The syntax for module function types are consistent with those for function types, but with types capitalized to represent the signatures of modules the module functions accepts as arguments and return values. In the previous example, we're exposing the backing type of a set; by giving `MakeSet` a module function signature, we can hide the underlying data structure! ```res module type Comparable = ... module type MakeSetType = (Item: Comparable) => { type backingType let empty: backingType let add: (backingType, Item.t) => backingType } module MakeSet: MakeSetType = (Item: Comparable) => { ... } ``` ```js // Empty output ``` ## Exotic Module Filenames **Since 8.3** It is possible to use non-conventional characters in your filenames (which is sometimes needed for specific JS frameworks). Here are some examples: - `src/Button.ios.res` - `pages/[id].res` Please note that modules with an exotic filename will not be accessible from other ReScript modules. ## Tips & Tricks Modules and module functions are at a different "layer" of language than the rest (functions, let bindings, data structures, etc.). For example, you can't easily pass them into a tuple or record. Use them judiciously, if ever! Lots of times, just a record or a function is enough.