# Overview ## ReScript Core [Core](api/core) is ReScript's new standard library. It replaces the complete `Js` module as well as some of the more frequently used modules from `Belt` and is recommended to use with uncurried mode. In ReScript 11, it is shipped as a separate npm package `@rescript/core` that is added to your project as per the [installation instructions](/docs/manual/latest/installation). In future ReScript versions, it will be included with the `rescript` npm package itself. ## Additional Libraries ReScript ships with these two additional modules in its standard library: - [Belt](api/belt): immutable collections and extra helpers not available in JavaScript / [Core](api/core). - [Dom](api/dom): Dom related types and modules. Contains our standardized types used by various userland DOM bindings. ## Legacy Modules The [Js](api/js) module is superseded by [Core](api/core). # Array and List ## Array Arrays are our main ordered data structure. They work the same way as JavaScript arrays: they can be randomly accessed, dynamically resized, updated, etc. ReScript arrays' items must have the same type, i.e. homogeneous. ### Usage #### Access Accessing items in an array will return an `option` and can be done like so: The behavior of returning an `option` is new to V11 when you have [Core](api/core) open. It provides a safer way to access array items, which is especially useful when you're not sure if the index is out of bounds. If you would like to not use an `option`, you can use [`Array.getUnsafe`](api/core/array#value-getUnsafe). #### Update Items in an array can be updated by assigning a value to an index or using a function: ### Array spreads **Since 11.1** You can spread arrays of the the same type into new arrays, just like in JavaScript: > Note that array spreads compiles to `Belt.Array.concatMany` right now. This is likely to change to native array spreads in the future. ## List ReScript provides a singly linked list too. Lists are: - immutable - fast at prepending items - fast at getting the head - slow at everything else Like arrays, lists' items need to be of the same type. ### Usage You'd use list for its resizability, its fast prepend (adding at the head), and its fast split, all of which are immutable and relatively efficient. Do **not** use list if you need to randomly access an item or insert at non-head position. Your code would end up obtuse and/or slow. The standard lib provides a [List module](api/core/list). #### Immutable Prepend Use the spread syntax: `myList` didn't mutate. `anotherList` is now `list{0, 1, 2, 3}`. This is efficient (constant time, not linear). `anotherList`'s last 3 elements are shared with `myList`! **Note that `list{a, ...b, ...c}` was a syntax error** before compiler v10.1. In general, the pattern should be used with care as its performance and allocation overhead are linear (`O(n)`). #### Access `switch` (described in the [pattern matching section](pattern-matching-destructuring.md)) is usually used to access list items:
# Async / Await ReScript comes with `async` / `await` support to make asynchronous, `Promise` based code easier to read and write. This feature is very similar to its JS equivalent, so if you are already familiar with JS' `async` / `await`, you will feel right at home. ## How it looks Let's start with a quick example to show-case the syntax: As we can see above, an `async` function is defined via the `async` keyword right before the function's parameter list. In the function body, we are now able to use the `await` keyword to explicitly wait for a `Promise` value and assign its content to a let binding `email`. You will probably notice that this looks very similar to `async` / `await` in JS, but there are still a few details that are specific to ReScript. The next few sections will go through all the details that are specific to the ReScript type system. ## Basics - You may only use `await` in `async` function bodies - `await` may only be called on a `promise` value - `await` calls are expressions, therefore they can be used in pattern matching (`switch`) - A function returning a `promise<'a>` is equivalent to an `async` function returning a value `'a` (important for writing signature files and bindings) - `promise` values and types returned from an `async` function don't auto-collapse into a "flat promise" like in JS (more on this later) ## Types and `async` functions ### `async` function type signatures Function type signatures (i.e defined in signature files) don't require any special keywords for `async` usage. Whenever you want to type an `async` function, use a `promise` return type. The same logic applies to type definitions in `.res` files: **BUT:** When typing `async` functions in your implementation files, you need to omit the `promise<'a>` type: For completeness reasons, let's expand the full signature and inline type definitions in one code snippet: **Note:** In a practical scenario you'd either use a type signature, or inline types, not both at the same time. In case you are interested in the design decisions, check out [this discussion](https://github.com/rescript-lang/rescript/pull/5913#issuecomment-1359003870). ### Promises don't auto-collapse in async functions In JS, nested promises (i.e. `promise>`) will automatically collapse into a flat promise (`promise<'a>`). This is not the case in ReScript. Use the `await` function to manually unwrap any nested promises within an `async` function instead. ## Error handling You may use `try / catch` or `switch` to handle exceptions during async execution. Note how we are essentially catching JS errors the same way as described in our [Exception](exception#catch-rescript-exceptions-from-js) section. You may unify error and value handling in a single switch as well: **Important:** When using `await` with a `switch`, always make sure to put the actual await call in the `switch` expression, otherwise your `await` error will not be caught. ## Piping `await` calls You may want to pipe the result of an `await` call right into another function. This can be done by wrapping your `await` calls in a new `{}` closure. Note how the original closure was removed in the final JS output. No extra allocations! ## Pattern matching on `await` calls `await` calls are just another kind of expression, so you can use `switch` pattern matching for more complex logic. ## `await` multiple promises We can utilize the `Promise` module to handle multiple promises. E.g. let's use `Promise.all` to wait for multiple promises before continuing the program: ## JS Interop with `async` functions `async` / `await` practically works with any function that returns a `promise<'a>` value. Map your `promise` returning function via an `external`, and use it in an `async` function as usual. Here's a full example of using the MDN `fetch` API, using `async` / `await` to simulate a login: # Attribute (Decorator) Like many other languages, ReScript allows annotating a piece of code to express extra functionality. Here's an example: The `@inline` annotation tells `mode`'s value to be inlined into its usage sites (see output). We call such annotation "attribute" (or "decorator" in JavaScript). An attribute starts with `@` and goes before the item it annotates. In the above example, it's hooked onto the let binding. ## Usage > **Note:** In previous versions (< 8.3) all our interop related attributes started with a `bs.` prefix (`bs.module`, `bs.val`). Our formatter will automatically drop them in newer ReScript versions. You can put an attribute almost anywhere. You can even add extra data to them by using them visually like a function call. Here are a few famous attributes (explained in other sections): 1. `@@warning("-27")` is a standalone attribute that annotates the entire file. Those attributes start with `@@`. Here, it carries the data `"-27"`. You can find a full list of all available warnings [here](./warning-numbers). 2. `@unboxed` annotates the type definition. 3. `@val` annotates the `external` statement. 4. `@as("aria-label")` annotates the `ariaLabel` record field. 5. `@deprecated` annotates the `customDouble` expression. This shows a warning while compiling telling consumers to not rely on this method long-term. 6. `@deprecated("Use SomeOther.customTriple instead")` annotates the `customTriple` expression with a string to describe the reason for deprecation. For a list of all decorators and their usage, please refer to the [Syntax Lookup](/syntax-lookup) page. ## Extension Point There's a second category of attributes, called "extension points" (a remnant term of our early systems): Extension points are attributes that don't _annotate_ an item; they _are_ the item. Usually they serve as placeholders for the compiler to implicitly substitute them with another item. Extension points start with `%`. A standalone extension point (akin to a standalone regular attribute) starts with `%%`. For a list of all extension points and their usage, please refer to the [Syntax Lookup](/syntax-lookup) page.# Bind to Global JS Values **First**, make sure the value you'd like to model doesn't already exist in our [provided API](api/core). Some JS values, like `setTimeout`, live in the global scope. You can bind to them like so: (We already provide `setTimeout`, `clearTimeout` and others in the [Core API](api/core) module). This binds to the JavaScript [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrworkerGlobalScope/setTimeout) methods and the corresponding `clearTimeout`. The `external`'s type annotation specifies that `setTimeout`: - Takes a function that accepts `unit` and returns `unit` (which on the JS side turns into a function that accepts nothing and returns nothing aka `undefined`), - and an integer that specifies the duration before calling said function, - returns a number that is the timeout's ID. This number might be big, so we're modeling it as a float rather than the 32-bit int. ### Tips & Tricks **The above isn't ideal**. See how `setTimeout` returns a `float` and `clearTimeout` accepts one. There's no guarantee that you're passing the float created by `setTimeout` into `clearTimeout`! For all we know, someone might pass it `Math.random()` into the latter. We're in a language with a great type system now! Let's leverage a popular feature to solve this problem: abstract types. Clearly, `timerId` is a type that can only be created by `setTimeout`! Now we've guaranteed that `clearTimeout` _will_ be passed a valid ID. Whether it's a number under the hood is now a mere implementation detail. Since `external`s are inlined, we end up with JS output as readable as hand-written JS. ## Global Modules If you want to bind to a value inside a global module, e.g. `Math.random`, attach a `scope` to your `val` external: you can bind to an arbitrarily deep object by passing a tuple to `scope`: This binds to `window.location.ancestorOrigins.length`. ## Special Global Values Global values like `__filename` and `__DEV__` don't always exist; you can't even model them as an `option`, since the mere act of referring to them in ReScript (then compiled into JS) would trigger the usual `Uncaught ReferenceError: __filename is not defined` error in e.g. the browser environment. For these troublesome global values, ReScript provides a special approach: `%external(a_single_identifier)`. That first line's `typeof` check won't trigger a JS ReferenceError. Another example: # Function Binding a JS function is like binding any other value: We also expose a few special features, described below. ## Labeled Arguments ReScript has [labeled arguments](function.md#labeled-arguments) (that can also be optional). These work on an `external` too! You'd use them to _fix_ a JS function's unclear usage. Assuming we're modeling this: It'd be nice if on ReScript's side, we can bind & call `draw` while labeling things a bit: We've compiled to the same function, but now the usage is much clearer on the ReScript side thanks to labels! Note that you can freely reorder the labels on the ReScript side; they'll always correctly appear in their declaration order in the JavaScript output: ## Object Method Functions attached to a JS objects (other than JS modules) require a special way of binding to them, using `send`: In a `send`, the object is always the first argument. Actual arguments of the method follow (this is a bit what modern OOP objects are really). ### Chaining Ever used `foo().bar().baz()` chaining ("fluent api") in JS OOP? We can model that in ReScript too, through the [pipe operator](pipe.md). ### Nested function call `@send` can also accept a `@scope(("itemOne","itemTwo"))` to access a function on a nested property. ## Variadic Function Arguments You might have JS functions that take an arbitrary amount of arguments. ReScript supports modeling those, under the condition that the arbitrary arguments part is homogenous (aka of the same type). If so, add `variadic` to your `external`. `module` will be explained in [Import from/Export to JS](import-from-export-to-js.md). ## Modeling Polymorphic Function Apart from the above special-case, JS functions in general are often arbitrarily overloaded in terms of argument types and number. How would you bind to those? ### Trick 1: Multiple `external`s If you can exhaustively enumerate the many forms an overloaded JS function can take, simply bind to each differently: Note how all three externals bind to the same JS function, `draw`. ### Trick 2: Polymorphic Variant + `unwrap` If you have the irresistible urge of saying "if only this JS function argument was a variant instead of informally being either `string` or `int`", then good news: we do provide such `external` features through annotating a parameter as a polymorphic variant! Assuming you have the following JS function you'd like to bind to: Here, `padding` is really conceptually a variant. Let's model it as such. Obviously, the JS side couldn't have an argument that's a polymorphic variant! But here, we're just piggy backing on poly variants' type checking and syntax. The secret is the `@unwrap` annotation on the type. It strips the variant constructors and compile to just the payload's value. See the output. ## Constrain Arguments Better Consider the Node `fs.readFileSync`'s second argument. It can take a string, but really only a defined set: `"ascii"`, `"utf8"`, etc. You can still bind it as a string, but we can use poly variants + `string` to ensure that our usage's more correct: - Attaching `@string` to the whole poly variant type makes its constructor compile to a string of the same name. - Attaching a `@as("bla")` to a constructor lets you customize the final string. And now, passing something like `"myOwnUnicode"` or other variant constructor names to `readFileSync` would correctly error. Aside from string, you can also compile an argument to an int, using `int` instead of `string` in a similar way: `onClosed` compiles to `0`, `onOpen` to `20` and `inBinary` to **`21`**. ## Unknown for type safety It is best practice to inspect data received from untrusted external functions to ensure it contains what you expect. This helps avoid run-time crashes and unexpected behavior. If you're certain about what an external function returns, simply assert the return value as `string` or `array` or whatever you want it to be. Otherwise use `unknown`. The ReScript type system will prevent you from using an `unknown` until you first inspect it and "convert" it using JSON parsing utilities or similar tools. Consider the example below of two external functions that access the value of a property on a JavaScript object. `getPropertyUnsafe` returns an `'a`, which means "anything you want it to be." ReScript allows you to use this value as a `string` or `array` or any other type. Quite convenient! But if the property is missing or contains something unexpected, your code might break. You can make the binding more safe by changing `'a` to `string` or `option<'a>`, but this doesn't completely eliminate the problem. The `getPropertySafe` function returns an `unknown`, which could be `null` or a `string` or anything else. But ReScript prevents you from using this value inappropriately until it has been safely parsed. ## Special-case: Event Listeners One last trick with polymorphic variants: ## Fixed Arguments Sometimes it's convenient to bind to a function using an `external`, while passing predetermined argument values to the JS function: The `@as("exit")` and the placeholder `_` argument together indicates that you want the first argument to compile to the string `"exit"`. You can also use any JSON literal with `as`: `` @as(json`true`) ``, `` @as(json`{"name": "John"}`) ``, etc. ## Ignore arguments You can also explicitly "hide" `external` function parameters in the JS output, which may be useful if you want to add type constraints to other parameters without impacting the JS side: **Note:** It's a pretty niche feature, mostly used to map to polymorphic JS APIs. ## Modeling `this`-based Callbacks Many JS libraries have callbacks which rely on this (the source), for example: Here, `this` would point to `x` (actually, it depends on how `onload` is called, but we digress). It's not correct to declare `x.onload` of type `(. unit) -> unit`. Instead, we introduced a special attribute, `this`, which allows us to type `x` as so: `@this` reserves the first parameter for the `this` value, and for arity of 0, there is no need for a redundant `unit` type. ## Function Nullable Return Value Wrapping For JS functions that return a value that can also be `undefined` or `null`, we provide `@return(...)`. To automatically convert that value to an `option` type (recall that ReScript `option` type's `None` value only compiles to `undefined` and not `null`). `return(nullable)` attribute will automatically convert `null` and `undefined` to `option` type. Currently 4 directives are supported: `null_to_opt`, `undefined_to_opt`, `nullable` and `identity`. `identity` will make sure that compiler will do nothing about the returned value. It is rarely used, but introduced here for debugging purpose. ## Tagged template functions **Since 11.1** **Experimental** You can easily bind to [JS tagged template functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates). Tag functions in JS expect as input an array of strings and variadic parameters for the arguments of the interpolation. To bind to those functions in ReScript, the binding signature must have two arrays as arguments, the first one being an array of strings and the second can be an array of anything. You add the `@taggedTemplate` annotation and you're good to go! Notice that it gets compiled to tagged template literals in JS, which allows to use JS tools that only work on the literals and not by calling directly the tag function. There are plenty of useful JS tools you can bind to, like [`gql`](https://github.com/apollographql/graphql-tag), [`sql`](https://github.com/porsager/postgres), [`css`](https://github.com/mayank99/ecsstatic) and a lot others! # Bind to JS Object JavaScript objects are a combination of several use-cases: - As a "record" or "struct" in other languages (like ReScript and C). - As a hash map. - As a class. - As a module to import/export. ReScript cleanly separates the binding methods for JS object based on these 4 use-cases. This page documents the first three. Binding to JS module objects is described in the [Import from/Export to JS](import-from-export-to-js.md) section. ## Bind to Record-like JS Objects ### Bind Using ReScript Record If your JavaScript object has fixed fields, then it's conceptually like a ReScript record. Since a ReScript record compiles to a clean JavaScript object, you can definitely type a JS object as a ReScript record! External is documented [here](external.md). `@module` is documented [here](import-from-export-to-js.md). If you want or need to use different field names on the ReScript and the JavaScript side, you can use the `@as` decorator: This is useful to map to JavaScript attribute names that cannot be expressed in ReScript (such as keywords). It is also possible to map a ReScript record to a JavaScript array by passing indices to the `@as` decorator: ### Bind Using ReScript Object Alternatively, you can use [ReScript object](object.md) to model a JS object too: ### Bind Using Special Getter and Setter Attributes Alternatively, you can use `get` and `set` to bind to individual fields of a JS object: You can also use `get_index` and `set_index` to access a dynamic property or an index: ## Bind to Hash Map-like JS Object If your JavaScript object: - might or might not add/remove keys - contains only values that are of the same type Then it's not really an object, it's a hash map. Use [Dict](api/core/dict), which contains operations like `get`, `set`, etc. and cleanly compiles to a JavaScript object still. ## Bind to a JS Object That's a Class Use `new` to emulate e.g. `new Date()`: You can chain `new` and `module` if the JS module you're importing is itself a class: # Browser Support & Polyfills ReScript compiles to JavaScript **ES5**, with the exception of optionally allowing to compile to ES6's module import & export. For [old browsers](https://caniuse.com/#search=typed%20array), you also need to polyfill TypedArray. The following standard library functions require it: - `Int64.float_of_bits` - `Int64.bits_of_float` - `Int32.float_of_bits` - `Int32.bits_of_float` If you don't use these functions, you're fine. Otherwise, it'll be a runtime failure. import dynamic from "next/dynamic"; export const Docson = dynamic( () => import("src/components/Docson").then((comp) => { return comp.make; }), { ssr: false, loading: () =>
Loading...
, } ); export default function BuildConfigurationSchemaPage() { return ; } # Configuration `rescript.json` (or `bsconfig.json` in versions prior ReScript 11) is the single, mandatory build meta file needed for `rescript`. **The complete configuration schema is [here](./build-configuration-schema)**. We'll _non-exhaustively_ highlight the important parts in prose below. ## name, namespace `name` is the name of the library, used as its "namespace". You can activate namespacing through `"namespace": true` in your `rescript.json`. Namespacing is almost **mandatory**; we haven't turned it on by default yet to preserve backward-compatibility. **Explanation**: by default, your files, once used as a third-party dependency, are available globally to the consumer. E.g. if you have a `Util.res` and the consumer also has a file of the same name, they will clash. Turning on `namespace` avoids this by wrapping all your own project's files into an extra module layer; instead of a global `Util` module, the consumer will see you as `MyProject.Util`. **The namespacing affects your consumers, not yourself**. Aka, in ReScript, "namespace" is just a fancy term for an auto-generated module that wraps all your project's files (efficiently and correctly, of course!) for third-party consumption. We don't do folder-level namespacing for your own project; all your own file names must be unique. This is a constraint that enables several features such as fast search and easier project reorganization. **Note**: the `rescript.json` `name` should be the same as the `package.json` `name`, to avoid confusing corner-cases. However, this means that you can't use a camelCased names such as `MyProject`, since `package.json` and npm forbid you to do so (some file systems are case-insensitive). To have the namespace/module as `MyProject`, write `"name": "my-project"`. ReScript will turn that into the camelCased name correctly. **Note on custom namespacing**: if for some reason, you need a namespace that is different from what your `name` will produce, you can directly send a string to the `namespace` option. For example, if your package is a binding named `bs-some-thing`, you can use `"namespace": "some-thing"` to get `SomeThing` namespace instead of `BsSomeThing`. ## sources Your source files need to be specified explicitly (we don't want to accidentally drill down into some unrelated directories). Examples: You can mark your directories as dev-only (for e.g. tests). These won't be built and exposed to third-parties, or even to other "dev" directories in the same project: You can also explicitly allow which modules can be seen from outside. This feature is especially useful for library authors who want to have a single entry point for their users. Here, the file `src/MyMainModule.res` is exposed to outside consumers, while all other files are private. ## bs-dependencies, bs-dev-dependencies List of ReScript dependencies. Just like `package.json`'s dependencies, they'll be searched in `node_modules`. Note that only sources marked with `"type":"dev"` will be able to resolve modules from `bs-dev-dependencies`. ## pinned-dependencies **Since 8.4**: List of pinned dependencies. A pinned dependency will always be rebuilt whenever you build a toplevel package (e.g. your main app) with `rescript`. This is useful for working on multiple independent ReScript packages simultaneously. More usage details can be found in our dedicated [pinned dependencies](./build-pinned-dependencies) page. ## external-stdlib **Since 9.0**: This setting allows depending on an externally built stdlib package (instead of a locally built stdlib runtime). Useful for shipping packages that are only consumed in JS or TS without any dependencies to the ReScript development toolchain. More details can be found on our [external stdlib](./build-external-stdlib) page. ## js-post-build Hook that's invoked every time a file is recompiled. Good for JS build system interop, but please use it **sparingly**. Calling your custom command for every recompiled file slows down your build and worsens the building experience for even third-party users of your lib. Example: Note that the path resolution for the command (`node` in this case) is done so: - `/myCommand` is resolved into `/myCommand` - `package/myCommand` is resolved into `node_modules/package/myCommand` - `./myCommand` is resolved into `myProjectRoot/myCommand` - `myCommand` is just called as `myCommand`, aka a globally available executable. But note that ReScript doesn't read into your shell's environment, so if you put e.g. `node`, it won't find it unless you specify an absolute path. Alternatively, add `#!/usr/local/bin/node` to the top of your script to directly call it without prepending `node`. The command itself is called from inside `lib/bs`. ## package-specs Output to either CommonJS (the default) or JavaScript module. Example: - `"module": "commonjs"` generates output as CommonJS format. - `"module": "esmodule"` generates output as JavaScript module format. Will be default value in next major. - `"in-source": true` generates output alongside source files. If you omit it, it'll generate the artifacts into `lib/js`. The output directory is not configurable otherwise. This configuration only applies to you, when you develop the project. When the project is used as a third-party library, the consumer's own `rescript.json` `package-specs` overrides the configuration here, logically. ## suffix **Since 11.0**: The suffix can now be freely chosen. However, we still suggest you stick to the convention and use one of the following: - `".js` - `".mjs"` - `".cjs"` - `".res.js"` - `".res.mjs"` - `".res.cjs"` ### Design Decisions Generating JS files with the `.res.js` suffix means that, on the JS side, you can do `const myReScriptFile = require('./TheFile.res.js')`. The benefits: - It's immediately clear that we're dealing with a generated JS file here. - It avoids clashes with a potential `TheFile.js` file in the same folder. - It avoids the need of using a build system loader for ReScript files. This + in-source build means integrating a ReScript project into your pure JS codebase **basically doesn't touch anything in your build pipeline at all**. ## uncurried **Since 11.0**: While we strongly encourage all users to use uncurried mode, it is still possible to opt out. Just set `"uncurried"` to `false` to get the old behavior back: More details can be found in the [blogpost about "Uncurried Mode"](/blog/uncurried-mode). ## warnings Selectively turn on/off certain warnings and/or turn them into hard errors. Example: Turn off warning `44` and `102` (polymorphic comparison). Turn warning `5` (partial application whose result has function type and is ignored) into a hard error. The warning numbers are shown in the build output when they're triggered. See [Warning Numbers](./warning-numbers) for the complete list. ## bsc-flags Extra flags to pass to the compiler. For advanced usages. - `-open ABC` opens the module `ABC` for each file in the project. `ABC` can either be a dependency, namespaced project or local module of the current project. ## gentypeconfig To enable genType, set `"gentypeconfig"` at top level in the project's `rescript.json`. `generatedFileExtension`: File extension used for genType generated files (defaults to `".gen.tsx"`) `module`: Module format used for the generated `*.gen.tsx` files (supports `"esmodule"` and `"commonjs"`) `moduleResolution`: Module resolution strategy used in genType outputs. This may be required for compatibility with TypeScript projects. Specify the value as the same in `tsconfig.json`. - `"node"`(default): Drop extensions in import paths. - `"node16"`: Use TS output's extension. This provides compatibility with projects using `"moduleResolution": "node16"` and ES Modules. - `"bundler"`: Use TS input's extension. This provides compatibility with projects using `"moduleResolution": "bundler"` and ES Modules. This also requires TS v5.0+ and `compilerOptions.allowImportingTsExtensions` to `true` `debug`: Enable debug logs. ### Deprecated options `language`: the `language` setting is not required from compiler v10.1. `shims`: Required only if one needs to export certain basic ReScript data types to JS when one cannot modify the sources to add annotations (e.g. exporting ReScript lists), and if the types are not first-classed in genType. ## Environment Variables We heavily disrecommend the usage of environment variables, but for certain cases, they're justified. ### Error Output Coloring: `NINJA_ANSI_FORCED` This is mostly for other programmatic usage of `rescript` where outputting colors is not desired. When `NINJA_ANSI_FORCED` is set to `1`: `rescript` produces color. When `NINJA_ANSI_FORCED` is set to `0`: `rescript` doesn't produce color. When `NINJA_ANSI_FORCED` is not set: `rescript` might or might not produce color, depending on a smart detection of where it's outputted. > Note that the underlying compiler will always be passed `-color always`. See more details in [this issue](https://github.com/rescript-lang/rescript/issues/2984#issuecomment-410669163). # External Stdlib **Since 9.0** Your ReScript project depends on the `rescript` package as a [`devDependency`](https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file), which includes our compiler, build system and runtime like `Belt`. However, you had to move it to `dependency` in `package.json` if you publish your code: - To Docker or other low-storage deployment devices. - For pure JS/TS consumers who probably won't install `rescript` in their own project. In these cases, the size or mere presence of `rescript` can be troublesome, since it includes not just our necessary runtime like `Belt`, but also our compiler and build system. To solve that, we now publish our runtime as a standalone package at [`@rescript/std`](https://www.npmjs.com/package/@rescript/std), whose versions mirror `rescript`'s. Now you can keep `rescript` as a `devDependency` and have only `@rescript/std` as your runtime `dependency`. **This is an advanced feature**. Please only use it in the aforementioned scenarios. If you already use a JS bundler with dead code elimination, you might not need this feature. ## Configuration Say you want to publish a JS-only ReScript 9.0 library. Install the packages like this: Then add this to `rescript.json`: Now the compiled JS code will import using the path defined by `external-stdlib`. Check the JS output tab: **Make sure the version number of `rescript` and `@rescript/std` match in your `package.json`** to avoid running into runtime issues due to mismatching stdlib assumptions. # Build System Overview ReScript comes with a build system, [`rescript`](https://www.npmjs.com/package/rescript), that's fast, lean and used as the authoritative build system of the community. Every ReScript project needs a build description file, `rescript.json`. ## Options See `rescript help`: ``` ❯ rescript help Usage: rescript `rescript` is equivalent to `rescript build` Options: -v, -version display version number -h, -help display help Subcommands: build clean format convert dump help Run `rescript -h` for subcommand help. Examples: rescript build -h rescript format -h ``` ## Build Project Each build will create build artifacts from your project's source files. **To build a project (including its dependencies / pinned-dependencies)**, run: Which is an alias for `rescript build`. To keep a build watcher, run: Any new file change will be picked up and the build will re-run. **Note**: third-party libraries (in `node_modules`, or via `pinned-dependencies`) aren't watched, as doing so may exceed the node.js watcher count limit. **Note 2**: In case you want to set up a project in a JS-monorepo-esque approach (`npm` and `yarn` workspaces) where changes in your sub packages should be noticed by the build, you will need to define pinned dependencies in your main project's `rescript.json`. More details [here](./build-pinned-dependencies). ## Clean Project If you ever get into a stale build for edge-case reasons, use: ## Compile with stricter errors in CI **Since 11.1** You may want to compile your project with stricter rules for production, than when developing. With the `-warn-error` build flag, this can easily be done, for instance in a continuous integration script. E.g.: Here, warning number 110, which is triggered when a [`%todo`](/syntax-lookup#todo) has been found, gets promoted to an error. The full list of warning numbers can be found [here](/docs/manual/latest/warning-numbers). # Build Performance ReScript considers performance at install time, build time and run time as a serious feature; it's one of those things you don't notice until you realize it's missing. ## Profile Your Build Sometime your build can be slow due to some confused infra setups. We provide an interactive visualization of your build's performance via `bstracing`: Run the above command at your ReScript project's root; it'll spit out a JSON file you can drag and drop into `chrome://tracing`. import Image from "src/components/Image"; ## Under the Hood ReScript itself uses a build system under the hood, called [Ninja](https://ninja-build.org). Ninja is like Make, but cross-platform, minimal, focuses on perf and destined to be more of a low-level building block than a full-blown build system. In this regard, Ninja's a great implementation detail for `rescript`. ReScript reads into `rescript.json` and generates the Ninja build file in `lib/bs`. The file contains the low-level compiler commands, namespacing rules, intermediate artifacts generation & others. It then runs `ninja` for the actual build. ## The JS Wrapper `rescript` itself is a Node.js wrapper which takes care of some miscellaneous tasks, plus the watcher. The lower-level, watcher-less, fast native `rescript` is called `rescript.exe`. It's located at `node_modules/rescript/{your-platform}/rescript.exe`. If you don't need the watcher, you can run said `rescript.exe`. This side-steps Node.js' long startup time, which can be in the order of `100ms`. Our editor plugin finds and uses this native `rescript.exe` for better performance. ## Numbers Raw `rescript.exe` build on a small project should be around `70ms`. This doubles when you use the JS `rescript` wrapper which comes with a watcher, which is practically faster since you don't manually run the build at every change (though you should opt for the raw `rescript.exe` for programmatic usage, e.g. inserting rescript into your existing JS build pipeline). No-op build (when no file's changed) should be around `15ms`. Incremental rebuild (described soon) of a single file in a project is around `70ms` too. Cleaning the artifacts should be instantaneous. ### Extreme Test We've stress-tested `rescript.exe` on a big project of 10,000 files (2 directories, 5000 files each, first 5000 no dependencies, last 5000 10 dependencies on files from the former directory) using https://github.com/rescript-lang/build-benchmark, on a Retina Macbook Pro Early 2015 (3.1 GHz Intel Core i7). - No-op build of 10k files: `800ms` (the minimum amount of time required to check the mtimes of 10k files). - Clean build: \<3 minutes. - Incremental build: depends on the number of the dependents of the file. No dependent means `1s`. ### Stability `rescript` is a file-based build system. We don't do in-memory build, even if that speeds up the build a lot. In-memory builds risk memory leaks, out-of-memory errors, corrupt halfway build and others. Our watcher mode stays open for days or months with no leak. The watcher is also just a thin file watcher that calls `rescript.exe`. We don't like babysitting daemon processes. ## Incrementality & Correctness ReScript doesn't take whole seconds to run every time. The bulk of the build performance comes from incremental build, aka re-building a previously built project when a few files changed. In short, thanks to our compiler and the build system's architecture, we're able to **only build what's needed**. E.g. if `MyFile.res` isn't changed, then it's not recompiled. You can roughly emulate such incrementalism in languages like JavaScript, but the degree of correctness is unfortunately low. For example, if you rename or move a JS file, then the watcher might get confused and not pick up the "new" file or fail to clean things up correctly, resulting in you needing to clean your build and restart anew, which defeats the purpose. Say goodbye to stale build from your JavaScript ecosystem! ## Speed Up Incremental Build ReScript uses the concept of interface files (`.resi`) (or, equivalently, [module signatures](module.md#signatures)). Exposing only what you need naturally speeds up incremental builds. E.g. if you change a `.res` file whose corresponding `.resi` file doesn't expose the changed part, then you've reduced the amount of dependent files you have to rebuild. ## Programmatic Usage Unfortunately, JS build systems are usually the bottleneck for building a JS project nowadays. Having parts of the build blazingly fast doesn't matter much if the rest of the build takes seconds or literally minutes. Here are a few suggestions: - Convert more files into ReScript =). Fewer files going through fewer parts of the JS pipeline helps a ton. - Careful with bringing in more dependencies: libraries, syntax transforms (e.g. the unofficially supported PPX), build step loaders, etc. The bulk of these dragging down the editing & building experience might out-weight the API benefits they provide. ## Hot Reloading Hot reloading refers to maintaining a dev server and listening to file changes in a way that allows the server to pipe some delta changes right into the currently running browser page. This provides a relatively fast iteration workflow while working in specific frameworks. However, hot reloading is fragile by nature, and counts on the occasional inconsistencies (bad state, bad eval, etc.) and the heavy devserver setup/config being less of a hassle than the benefits it provides. We err on the side of caution and stability in general, and decided not to provide a built-in hot reloading _yet_. **Note**: you can still use the hot reloading facility provided by your JS build pipeline. # Pinned Dependencies Usually we'd recommend to use ReScript in a single-codebase style by using one `rescript.json` file for your whole codebase. There are scenarios where you still want to connect and build multiple independent ReScript packages for one main project though (`npm` workspaces-like "monorepos"). This is where `pinned-dependencies` come into play. ## Package Types Before we go into detail, let's first explain all the different package types recognized by the build system: - Toplevel (this is usually the final app you are building, which has dependencies to other packages) - Pinned dependencies (these are your local packages that should always rebuild when you build your toplevel, those should be listed in `bs-dependencies` and `pinned-dependencies`) - Normal dependencies (these are packages that are consumed from npm and listed via `bs-dependencies`) Whenever a package is being built (`rescript build`), the build system will build the toplevel package with its pinned-dependencies. So any changes made in a pinned dependency will automatically be reflected in the final app. ## Build System Package Rules The build system respects the following rules for each package type: **Toplevel** - Warnings reported - Warn-error respected - Builds dev dependencies - Builds pinned dependencies - Runs custom rules - Package-specs like JavaScript module or CommonJS overrides all its dependencies **Pinned dependencies** - Warnings reported - Warn-error respected - Ignores pinned dependencies - Builds dev dependencies - Runs custom rules **Normal dependencies** - Warnings, warn-error ignored - Ignores dev directories - Ignores pinned dependencies - Ignores custom generator rules So with that knowledge in mind, let's dive into some more concrete examples to see our pinned dependencies in action. ## Examples ### Yarn workspaces Let's assume we have a codebase like this: ``` myproject/ app/ - src/App.res - rescript.json common/ - src/Header.res - rescript.json myplugin/ - src/MyPlugin.res - rescript.json package.json ``` Our `package.json` file within our codebase root would look like this: Our `app` folder would be our toplevel package, consuming our `common` and `myplugin` packages as `pinned-dependencies`. The configuration for `app/rescript.json` looks like this: Now, whenever we are running `rescript build` within our `app` package, the compiler would always rebuild any changes within its pinned dependencies as well. **Important:** ReScript will not rebuild any `pinned-dependencies` in watch mode! This is due to the complexity of file watching, so you'd need to set up your own file-watcher process that runs `rescript build` on specific file changes. # If-Else & Loops ReScript supports `if`, `else`, ternary expression (`a ? b : c`), `for` and `while`. ReScript also supports our famous pattern matching, which will be covered in [its own section](pattern-matching-destructuring.md) ## If-Else & Ternary Unlike its JavaScript counterpart, ReScript's `if` is an expression; they evaluate to their body's content: **Note:** an `if-else` expression without the final `else` branch implicitly gives `()` (aka the `unit` type). So this: is basically the same as: Here's another way to look at it. This is clearly wrong: It'll give a type error, saying basically that the implicit `else` branch has the type `unit` while the `if` branch has type `int`. Intuitively, this makes sense: what would `result`'s value be, if `showMenu` was `false`? We also have ternary sugar, but **we encourage you to prefer if-else when possible**. **`if-else` and ternary are much less used** in ReScript than in other languages; [Pattern-matching](pattern-matching-destructuring.md) kills a whole category of code that previously required conditionals. ## For Loops For loops iterate from a starting value up to (and including) the ending value. You can make the `for` loop count in the opposite direction by using `downto`. ## While Loops While loops execute its body code block while its condition is true. ### Tips & Tricks There's no loop-breaking `break` keyword (nor early `return` from functions, for that matter) in ReScript. However, we can break out of a while loop easily through using a [mutable binding](mutation.md). # Converting from JS ReScript offers a unique project conversion methodology which: - Ensures minimal disruption to your teammates (very important!). - Remove the typical friction of verifying conversion's correctness and performance guarantees. - Doesn't force you to search for pre-made binding libraries made by others. **ReScript doesn't need the equivalent of TypeScript's `DefinitelyTyped`**. ## Step 1: Install ReScript Run `npm install rescript` on your project, then imitate our [New Project](installation#new-project) workflow by adding a `rescript.json` at the root. Then start `npx rescript -w`. ## Step 2: Copy Paste the Entire JS File Let's work on converting a file called `src/main.js`. First, copy the entire file content over to a new file called `src/Main.res` by using our [`%%raw` JS embedding trick](embed-raw-javascript): Add this file to `rescript.json`: Open an editor tab for `src/Main.res.js`. Do a command-line `diff -u src/main.js src/Main.res.js`. Aside from whitespaces, you should see only minimal, trivial differences. You're already a third of the way done! **Always make sure** that at each step, you keep the ReScript output `.res.js` file open to compare against the existing JavaScript file. Our compilation output is very close to your hand-written JavaScript; you can simply eye the difference to catch conversion bugs! ## Step 3: Extract Parts into Idiomatic ReScript Let's turn the `defaultId` variable into a ReScript let-binding: Check the output. Diff it. Code still works. Moving on! Extract the function: Format the code: `./node_modules/.bin/rescript format src/Main.res`. We have a type error: "The record field student can't be found". That's fine! **Always ensure your code is syntactically valid first**. Fixing type errors comes later. ## Step 4: Add `external`s, Fix Types The previous type error is caused by `payload`'s record declaration (which supposedly contains the field `student`) not being found. Since we're trying to convert as quickly as possible, let's use our [object](object) feature to avoid needing type declaration ceremonies: Now this triggers the next type error, that `school` isn't found. Let's use [`external`](external) to bind to that module: We hurrily typed `school` as a polymorphic `'whatever` and let its type be inferred by its usage below. The inference is technically correct, but within the context of bringing it a value from JavaScript, slightly dangerous. This is just the interop trick we've shown in the [`external`](external) page. Anyway, the file passes the type checker again. Check the `.res.js` output, diff with the original `.js`; we've now converted a file over to ReScript! Now, you can delete the original, hand-written `main.js` file, and grep the files importing `main.js` and change them to importing `Main.res.js`. ## (Optional) Step 5: Cleanup If you prefer more advanced, rigidly typed `payload` and `school`, feel free to do so: We've: - introduced an opaque types for `school` and `student` to prevent misuse of their values - typed the payload as a record with only the `student` field - typed `getStudentById` as the sole method of `student` Check that the `.res.js` output didn't change. How rigidly to type your JavaScript code is up to you; we recommend not typing them too elaborately; it's sometime an endless chase, and produces diminishing returns, especially considering that the elaborate-ness might turn off your potential teammates. ## Tips & Tricks In the same vein of idea, **resist the urge to write your own wrapper functions for the JS code you're converting**. Use [`external`s](external), which are guaranteed to be erased in the output. And avoid trying to take the occasion to convert JS data structures into ReScript-specific data structures like variant or list. **This isn't the time for that**. The moment you produce extra conversion code in the output, your skeptical teammate's mental model might switch from "I recognize this output" to "this conversion might be introducing more problems than it solves. Why are we testing ReScript again?". Then you've lost. ## Conclusion - Paste the JS code into a new ReScript file as embedded raw JS code. - Compile and keep the output file open. Check and diff against original JS file. Free regression tests. - Always make sure your file is syntactically valid. Don't worry about fixing types before that. - (Ab)use [object](object.md) accesses to quickly convert things over. - Optionally clean up the types for robustness. - Don't go overboard and turn off your boss and fellow teammates. - Proudly display that you've conserved the semantics and performance characteristics during the conversion by showing your teammates the eerily familiar output. - Get promoted for introducing a new technology the safer, mature way. # Editor Plugins - [VSCode](https://marketplace.visualstudio.com/items?itemName=chenglou92.rescript-vscode) - [Sublime Text](https://github.com/rescript-lang/rescript-sublime) - [Vim/Neovim](https://github.com/rescript-lang/vim-rescript) ## Community Supported We don't officially support these; use them at your own risk! - [Neovim Tree-sitter](https://github.com/nkrkv/nvim-treesitter-rescript) - [IDEA](https://github.com/reasonml-editor/reasonml-idea-plugin) - [Emacs](https://github.com/jjlee/rescript-mode) - [Zed](https://github.com/humaans/rescript-zed) # Embed Raw JavaScript ## Paste Raw JS Code First thing first. If you're ever stuck learning ReScript, remember that you can always just paste raw JavaScript code into our source file: The `%%raw` special ReScript call takes your code string and pastes it as-is into the output. **You've now technically written your first ReScript file!** (The back tick syntax is a multiline string, similar to JavaScript's. Except for us, no escaping is needed inside the string. More on string in a later section.) While `%%raw` lets you embed top-level raw JS code, `%raw` lets you embed expression-level JS code: The above code: - declared a ReScript variable `add`, - with the raw JavaScript value of a function declaration, - then called that function in ReScript. If your boss is ever worried that your teammates can't adopt ReScript, just let them keep writing JavaScript inside ReScript files =). ## Debugger You can also drop a `%debugger` expression in a body: Output: ## Tips & Tricks Embedding raw JS snippets isn't the best way to experience ReScript, though it's also highly useful if you're just starting out. As a matter of fact, the first few ReScript projects were converted through: - pasting raw JS snippets inside a file - examining the JS output (identical to the old hand-written JS) - gradually extract a few values and functions and making sure the output still looks OK At the end, we get a fully safe, converted ReScript file whose JS output is clean enough that we can confidently assert that no new bug has been introduced during the conversion process. We have a small guide on this iteration [here](converting-from-js.md). Feel free to peruse it later. # Equality and Comparison ReScript has shallow equality `===`, deep equality `==`, and comparison operators `>`, `>=`, `<`, and `<=`. ## Shallow equality The shallow equality operator `===` compares two values and either compiles to `===` or a `bool` if the equality is known to the compiler. It behaves the same as the strict equality operator `===` in JavaScript. Using `===` will never add a runtime cost. ## Deep equality ReScript has the deep equality operator `==` to check deep equality of two items, which is very different from the loose equality operator like `==` in JavaScript. When using `==` in ReScript it will never compile to `==` in JavaScript, it will either compile to `===`, a runtime call to an internal function that deeply compares the equality, or a `bool` if the equality is known to the compiler. `==` will compile to `===` (or a `bool` if the compiler can determine equality) when: - Comparing `string`, `char`, `int`, `float`, `bool`, or `unit` - Comparing variants or polymorphic variants that do not have constructor values `==` will compile to a runtime check for deep equality when: - Comparing `array`, `tuple`, `list`, `object`, `record`, or regular expression `Re.t` - Comparing variants or polymorphic variants that have constructor values > When using `==` pay close attention to the JavaScript output if you're not sure what `==` will compile to. ## Comparison ReScript has operators for comparing values that compile to the the same operator in JS, a runtime check using an internal function, or a `bool` if the equality is known to the compiler, | operator | comparison | | --- | ----------- | | `>` | greater than | | `>=` | greater than or equal | | `<` | less than | | `<=` | less than or equal | Comparison can be done on any type. An operator will compile to the same operator (or a `bool` if the compiler can determine equality) when: - Comparing `int`, `float`, `string`, `char`, `bool` An operator will compile to a runtime check for deep equality when: - Comparing `array`, `tuple`, `list`, `object`, `record`, or regular expression (`Re.t`) - Comparing variants or polymorphic variants ## Performance of runtime equality checks The runtime equality check ReScript uses is quite fast and should be adequate for almost all use cases. For small objects it can be 2x times faster than alternative deep compare functions such as Lodash's [`_.isEqual`](https://lodash.com/docs/4.17.15#isEqual). For larger objects instead of using `==` you could manually use a faster alternative such as [fast-deep-compare](https://www.npmjs.com/package/fast-deep-equal), or write a custom comparator function. [This repo](https://github.com/jderochervlk/rescript-perf) has benchmarks comparing results of different libraries compared to ReScript's built-in equality function.# Exception Exceptions are just a special kind of variant, thrown in **exceptional** cases (don't abuse them!). Consider using the [`option`](null-undefined-option.mdx) or [`result`](api/core/result) type for recoverable errors. You can create your own exceptions like you'd make a variant (exceptions need to be capitalized too). ## Built-in Exceptions ReScript has some built-in exceptions: ### `Not_found` Note that the above is just for demonstration purposes; in reality, you'd return an `option` directly from `getItem` and avoid the `try` altogether. You can directly match on exceptions _while_ getting another return value from a function: ### `Invalid_argument` Used to check if argument is valid. This exception takes a string. ### `Assert_failure` Raise when you use `assert(condition)` and `condition` is false. The arguments are the location of the `assert` in the source code (file name, line number, column number). ### `Failure` Exception raised to signal that the given arguments do not make sense. This exception takes a string as an argument. ### `Division_by_zero` Exception raised by integer division and remainder operations when their second argument is zero. ## Catching JS Exceptions To distinguish between JavaScript exceptions and ReScript exceptions, ReScript namespaces JS exceptions under the `Exn.Error(payload)` variant. To catch an exception thrown from the JS side: Throw an exception from JS: Then catch it from ReScript: The `obj` here is of type `Exn.t`, intentionally opaque to disallow illegal operations. To operate on `obj`, do like the code above by using the standard library's [`Exn`](api/js/exn) module's helpers. ## Raise a JS Exception `raise(MyException)` raises a ReScript exception. To raise a JavaScript exception (whatever your purpose is), use `Exn.raiseError`: Then you can catch it from the JS side: ## Catch ReScript Exceptions from JS The previous section is less useful than you think; to let your JS code work with your exception-throwing ReScript code, the latter doesn't actually need to throw a JS exception. ReScript exceptions can be used by JS code! Then, in your JS: > Note: `RE_EXN_ID` is an internal field for bookkeeping purposes. Don't use it on the JS side. Use the other fields. The above `BadArgument` exception takes an inline record type. We special-case compile the exception as `{RE_EXN_ID, myMessage, Error}` for good ergonomics. If the exception instead took ordinary positional arguments, l like the standard library's `Invalid_argument("Oops!")`, which takes a single argument, the argument is compiled to JS as the field `_1` instead. A second positional argument would compile to `_2`, etc. ## Tips & Tricks When you have ordinary variants, you often don't **need** exceptions. For example, instead of throwing when `item` can't be found in a collection, try to return an `option` (`None` in this case) instead. ### Catch Both ReScript and JS Exceptions in the Same `catch` Clause This technically works, but hopefully you don't ever have to work with such code... # Extensible Variant Variant types are usually constrained to a fixed set of constructors. There may be very rare cases where you still want to be able to add constructors to a variant type even after its initial type declaration. For this, we offer extensible variant types. ## Definition and Usage The `..` in the type declaration above defines an extensible variant `type t`. The `+=` operator is then used to add constructors to the given type. **Note:** Don't forget the leading `type` keyword when using the `+=` operator! ## Pattern Matching Caveats Extensible variants are open-ended, so the compiler will not be able to exhaustively pattern match all available cases. You will always need to provide a default `_` case for every `switch` expression. ## Tips & Tricks **Fun fact:** In ReScript, [exceptions](./exception) are actually extensible variants under the hood, so `exception UserError(string)` is equivalent to `type exn += UserError(string)`. It's one of the very few use-case where extensible variants make sense. We usually recommend sticking with common [variants](./variant) as much as possible to reap the benefits of exhaustive pattern matching.# External (Bind to Any JS Library) `external` is the primary ReScript features for bringing in and using JavaScript values. `external` is like a let binding, but: - The right side of `=` isn't a value; it's the name of the JS value you're referring to. - The type for the binding is mandatory, since we need to know what the type of that JS value is. - Can only exist at the top level of a file or module. There are several kinds of `external`s, differentiated and/or augmented through the [attribute](attribute.md) they carry. This page deals with the general, shared mechanism behind most `external`s. The different `external`s are documented in their respective pages later. A few notable ones: - `@val`, `@scope`: [bind to global JS values](bind-to-global-js-values). - `@module`: [bind to JS imported/exported values](import-from-export-to-js). - `@send`: [bind to JS methods](bind-to-js-function). You can also use our [Syntax Lookup](/syntax-lookup) tool to find them. Related: see also our [list of external decorators](interop-cheatsheet#list-of-decorators). ## Usage Once declared, you can use an `external` as a normal value, just like a let binding. ## Tips & Tricks `external` + ReScript objects are a wonderful combination for quick prototyping. Check the JS output tab: We've specified `document`'s type as `'a`, aka a placeholder type that's polymorphic. Any value can be passed there, so you're not getting much type safety (except the inferences at various call sites). However, this is excellent for quickly getting started using a JavaScript library in ReScript **without needing the equivalent of a repository of typed bindings** like TypeScript's `DefinitelyTyped` repo. However, if you want to more rigidly bind to the JavaScript library you want, keep reading the next few interop pages. ## Performance & Output Readability `external`s declarations are inlined into their callers during compilation, **and completely disappear from the JS output**. This means any time you use one, you can be sure that you're not incurring extra JavaScript \<-> ReScript conversion cost. Additionally, no extra ReScript-specific runtime is better for output readability. > **Note:** do also use `external`s and the `@blabla` attributes in the interface files. Otherwise the inlining won't happen. ## Design Decisions ReScript takes interoperating with existing code very seriously. Our type system has very strong guarantees. However, such strong feature also means that, without a great interop system, it'd be very hard to gradually convert a codebase over to ReScript. Fortunately, our interop are comprehensive and cooperate very well with most existing JavaScript code. The combination of a sound type system + great interop means that we get the benefits of a traditional gradual type system regarding incremental codebase coverage & conversion, without the downside of such gradual type system: complex features to support existing patterns, slow analysis, diminishing return in terms of type coverage, etc. # Frequently Asked Questions **What's the goal of this project?** We aim to provide the best typed language experience for the JavaScript platform. **What’s the relationship with BuckleScript?** BuckleScript is ReScript's old branding, with a sharper focus on proper JS support and familiarity which we previously couldn't achieve to the degree we wanted, due to us needing to cater to various different crowds. **What’s ReScript's relationship with OCaml?** We reuse and adjust the excellent type system and lots of other high quality components from OCaml for JS experience. Additionally, ReScript provides its own syntax, build system, IDE, backend, JS interop, extra language features, etc. The ReScript toolchain is developed using OCaml, however, the version of ReScript is decoupled against the version of OCaml, ReScript compiler should build against any reasonable modern version of OCaml compiler. For the majority of ReScript users, they don't need to learn OCaml or use OCaml toolchain to be productive in ReScript. **What’s the relationship with Reason?** See [here](/blog/bucklescript-is-rebranding). Reason is a syntax layer for OCaml that BuckleScript also adopted. The current ReScript compiler also supports the old Reason syntax v3.6 for backward compatibility. We will support it for a long time to make sure existing users do not get breaking changes. **I come from Reason/OCaml. Will ReScript keep supporting X?** Please see our [blog post](/blog/a-note-on-bucklescripts-future-commitments) on this matter. **Where can I see the docs in old Reason/OCaml syntax?** Switch the doc version to `v8.0.0` in the sidebar on the left! **Will ReScript support native compilation eventually?** Our focus is a solid JS story right now. In the future, if there’s strong demand, we might consider it. **What’s the current state of ReScript?** Currently, we're actively working on the editor support. **When will we get the `async/await` keywords?** async/await has arrived in ReScript 10.1! **Why create a new syntax?** The existing Reason syntax is owned by a different team with a different vision. Reason aims to be 100% compatible with OCaml syntax and to support all versions of OCaml. In the last few years, we've drawn the conclusion that it’s very hard to deliver such goal without sacrificing user experience. The other reason is that we feel it’s better to have the same vision as a team so that we can make more coherent decisions. **Who is behind the project?** The ReScript team (Hongbo, Cheng, Cristiano, Maxim, Patrick, Ricky). **We have a new forum; will we also have our own Discord?** Not now. We've found that too much important information get casually passed in Discord then lost within the noise. We prefer folks to communicate on the [forum](https://forum.rescript-lang.org). This is nicer to the less active members. The team doesn't use the old Discord anymore. We encourage you to move your questions to the forum instead. # Function _Cheat sheet for the full function syntax at the end_. ReScript functions are declared with an arrow and return an expression, just like JS functions. They compile to clean JS functions too. This declares a function and assigns to it the name `greet`, which you can call like so: Multi-arguments functions have arguments separated by comma: For longer functions, you'd surround the body with a block: If your function has no argument, just write `let greetMore = () => {...}`. ## Labeled Arguments Multi-arguments functions, especially those whose arguments are of the same type, can be confusing to call. You can attach labels to an argument by prefixing the name with the `~` symbol: You can provide the arguments in **any order**: The `~x` part in the declaration means the function accepts an argument labeled `x` and can refer to it in the function body by the same name. You can also refer to the arguments inside the function body by a different name for conciseness: As a matter of fact, `(~radius)` is just a shorthand for `(~radius as radius)`. Here's the syntax for typing the arguments: ## Optional Labeled Arguments Labeled function arguments can be made optional during declaration. You can then omit them when calling the function. When given in this syntax, `radius` is **wrapped** in the standard library's `option` type, defaulting to `None`. If provided, it'll be wrapped with a `Some`. So `radius`'s type value is `None | Some(int)` here. More on `option` type [here](null-undefined-option.md). ### Signatures and Type Annotations Functions with optional labeled arguments can be confusing when it comes to signature and type annotations. Indeed, the type of an optional labeled argument looks different depending on whether you're calling the function, or working inside the function body. Outside the function, a raw value is either passed in (`int`, for example), or left off entirely. Inside the function, the parameter is always there, but its value is an option (`option`). This means that the type signature is different, depending on whether you're writing out the function type, or the parameter type annotation. The first being a raw value, and the second being an option. If we get back to our previous example and both add a signature and type annotations to its argument, we get this: The first line is the function's signature, we would define it like that in an interface file (see [Signatures](module.md#signatures)). The function's signature describes the types that the **outside world** interacts with, hence the type `int` for `radius` because it indeed expects an `int` when called. In the second line, we annotate the arguments to help us remember the types of the arguments when we use them **inside** the function's body, here indeed `radius` will be an `option` inside the function. So if you happen to struggle when writing the signature of a function with optional labeled arguments, try to remember this! ### Explicitly Passed Optional Sometimes, you might want to forward a value to a function without knowing whether the value is `None` or `Some(a)`. Naively, you'd do: This quickly gets tedious. We provide a shortcut: This means "I understand `radius` is optional, and that when I pass it a value it needs to be an `int`, but I don't know whether the value I'm passing is `None` or `Some(val)`, so I'll pass you the whole `option` wrapper". ### Optional with Default Value Optional labeled arguments can also be provided a default value. In this case, they aren't wrapped in an `option` type. ## Recursive Functions ReScript chooses the sane default of preventing a function to be called recursively within itself. To make a function recursive, add the `rec` keyword after the `let`: A simple recursive function may look like this: Recursively calling a function is bad for performance and the call stack. However, ReScript intelligently compiles [tail recursion](https://stackoverflow.com/questions/33923/what-is-tail-recursion) into a fast JavaScript loop. Try checking the JS output of the above code! ### Mutually Recursive Functions Mutually recursive functions start like a single recursive function using the `rec` keyword, and then are chained together with `and`: ## Partial Application **Since 11.0** To partially apply a function, use the explicit `...` syntax. ## Async/Await Just as in JS, an async function can be declared by adding `async` before the definition, and `await` can be used in the body of such functions. The output looks like idiomatic JS: The return type of `getUser` is inferred to be `promise`. Similarly, `await getUserName(userId)` returns a `string` when the function returns `promise`. Using `await` outside of an `async` function (including in a non-async callback to an async function) is an error. ### Ergonomic error handling Error handling is done by simply using `try`/`catch`, or a switch with an `exception` case, just as in functions that are not async. Both JS exceptions and exceptions defined in ReScript can be caught. The compiler takes care of packaging JS exceptions into the builtin `JsError` exception: ## The ignore() Function Occasionally you may want to ignore the return value of a function. ReScript provides an `ignore()` function that discards the value of its argument and returns `()`: ## Tips & Tricks Cheat sheet for the function syntaxes: ### Declaration #### With Type Annotation ### Application #### With Type Annotation ### Standalone Type Signature #### In Interface Files To annotate a function from the implementation file (`.res`) in your interface file (`.resi`): The type annotation part is the same as the previous section on With Type Annotation. **Don't** confuse `let add: myType` with `type add = myType`. When used in `.resi` interface files, the former exports the binding `add` while annotating it as type `myType`. The latter exports the type `add`, whose value is the type `myType`. # Generate Converters & Helpers **Note**: if you're looking for: - `@deriving(jsConverter)` for records - `@deriving({jsConverter: newType})` for records - `@deriving(abstract)` for records - `@deriving(jsConverter)` for plain and polymorphic variants These particular ones are no longer needed. Select a doc version lower than `9.0` in the sidebar to see their old docs. When using ReScript, you will sometimes come into situations where you want to - Automatically generate functions that convert between ReScript's internal and JS runtime values (e.g. variants). - Convert a record type into an abstract type with generated creation, accessor and method functions. - Generate some other helper functions, such as functions from record attribute names. You can use the `@deriving` decorator for different code generation scenarios. All different options and configurations will be discussed on this page. **Note:** Please be aware that extensive use of code generation might make it harder to understand your programs (since the code being generated is not visible in the source code, and you just need to know what kind of functions / values a decorator generates). ## Generate Functions & Plain Values for Variants Use `@deriving(accessors)` on a variant type to create accessor functions for its constructors. Variants constructors with payloads generate functions, payload-less constructors generate plain integers (the internal representation of variants). **Note**: - The generated accessors are lower-cased. - You can now use these helpers on the JavaScript side! But don't rely on their actual values please. ### Usage This is useful: - When you're passing the accessor function as a higher-order function (which plain variant constructors aren't). - When you'd like the JS side to use these values & functions opaquely and pass you back a variant constructor (since JS has no such thing). Please note that in case you just want to _pipe a payload into a constructor_, you don't need to generate functions for that. Use the `->` syntax instead, e.g. `"test"->Submit`. ## Generate Field Accessors for Records Use `@deriving(accessors)` on a record type to create accessors for its record field names. # Import & Export ## Import a Module/File Unlike JavaScript, ReScript doesn't have or need import statements: The above code refers to the `message` binding in the file `Student.res`. Every ReScript file is also a module, so accessing another file's content is the same as accessing another module's content! A ReScript project's file names need to be unique. ## Export Stuff By default, every file's type declaration, binding and module is exported, aka publicly usable by another file. **This also means those values, once compiled into JS, are immediately usable by your JS code**. To only export a few selected things, use a `.resi` [interface file](module.md#signatures). ## Work with JavaScript Import & Export To see how to import JS modules and export stuff for JS consumption, see the JavaScript Interop section's [Import from/Export to JS](import-from-export-to-js.md). # Import from/Export to JS You've seen how ReScript's idiomatic [Import & Export](import-export.md) works. This section describes how we work with importing stuff from JavaScript and exporting stuff for JavaScript consumption. If you're looking for react-specific interop guidance, check out the [React JS Interop guide](../../react/latest/import-export-reactjs.mdx). **Note**: due to JS ecosystem's module compatibility issues, our advice of keeping your ReScript file's compiled JS output open in a tab applies here **more than ever**, as you don't want to subtly output the wrong JS module import/export code, on top of having to deal with Babel/Webpack/Jest/Node's CommonJS \<-> JavaScript module compatibility shims. In short: **make sure your bindings below output what you'd have manually written in JS**. ## Output Format We support 2 JavaScript import/export formats: - JavaScript module: `import * from 'MyReScriptFile'` and `export let ...`. - CommonJS: `require('myFile')` and `module.exports = ...`. The format is [configurable in via `rescript.json`](build-configuration.md#package-specs). ## Import From JavaScript ### Import a JavaScript Module's Named Export Use the `module` [external](external.md): Here's what the `external` does: - `@module("path")`: pass the name of the JS module; in this case, `"path"`. The string can be anything: `"./src/myJsFile"`, `"@myNpmNamespace/myLib"`, etc. - `external`: the general keyword for declaring a value that exists on the JS side. - `dirname`: the binding name you'll use on the ReScript side. - `string => string`: the type signature of `dirname`. Mandatory for `external`s. - `= "dirname"`: the name of the variable inside the `path` JS module. There's repetition in writing the first and second `dirname`, because sometime the binding name you want to use on the ReScript side is different than the variable name the JS module exported. ### Import a JavaScript Module As a Single Value By omitting the string argument to `module`, you bind to the whole JS module: Depending on whether you're compiling ReScript to JavaScript module or CommonJS, **this feature will generate subtly different code**. Please check both output tabs to see the difference. The JavaScript module output here would be wrong! ### Import an `default` Export Use the value `default` on the right hand side: ### Use Import Attributes **Since 11.1** [Import attributes](https://github.com/tc39/proposal-import-attributes) can be used in ReScript, as long as ReScript is configured to output JavaScript module. You do that by passing configuration to the `@module` attribute: This above imports the local `./myJson.json` file, adding import attributes. This is how it works: 1. Instead of passing a string or tuple to `@module`, pass a record. 2. This record should have a `from` key. The value of that is where you want the module to be imported from (just like the regular string to `@module` is). 3. It should also have a `with` key, with another record where you put all the import attributes you want emitted. Notice `\"some-exotic-identifier"` - you'll need to escape any key that's not a valid ReScript record key. Also notice `type_`. Since `type` is a reserved keyword in ReScript, you can use `type_` instead. It will be output as `type` in the JavaScript code. ## Dynamic Import Leveraging JavaScript's [dynamic `import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) to reduce bundle size and lazy load code as needed is easy in ReScript. It's also a little bit more convenient than in regular JavaScript because you don't need to keep track of file paths manually with ReScript's module system. ### Dynamically Importing Parts of a Module Use the `import` function to dynamically import a specific part of a module. Put whatever `let` binding you want to import in there, and you'll get a `promise` back resolving to that specific binding. Let's look at an example. Imagine the following file `MathUtils.res`: Now let's dynamically import the add function in another module, e.g. `App.res`: ### Dynamically Importing an Entire Module The syntax for importing a whole module looks a little different, since we are operating on the module syntax level; instead of using `import`, you may simply `await` the module itself: ## Export To JavaScript ### Export a Named Value As mentioned in ReScript's idiomatic [Import & Export](import-export.md), every let binding and module is exported by default to other ReScript modules (unless you use a `.resi` [interface file](module#signatures)). If you open up the compiled JS file, you'll see that these values can also directly be used by a _JavaScript_ file too. ### Export a `default` Value If your JS project uses JavaScript module, you're likely exporting & importing some default values: A JavaScript default export is really just syntax sugar for a named export implicitly called `default` (now you know!). So to export a default value from ReScript, you can just do: You can then import this default export as usual on the JS side: If your JavaScript's default import is transpiled by Babel/Webpack/Jest into CommonJS `require`s, we've taken care of that too! See the CommonJS output tab for `__esModule`. # Inlining Constants Sometimes, in the JavaScript output, you might want a certain value to be forcefully inlined. For example: The reason is that your JavaScript bundler (e.g. Webpack) might turn that into: Then your subsequent Uglifyjs optimization would remove that entire `if` block. This is how projects like ReactJS provide a development mode code with plenty of dev warnings, while ensuring that the uglified (minified) production code is free of those expensive blocks. So, in ReScript, producing that example `if (process.env.mode === 'development')` output is important. This first try doesn't work: The JS output shows `if (process.env.mode === mode)`, which isn't what we wanted. To inline `mode`'s value, use `@inline`: Now your resulting JS code can pass through Webpack and Uglifyjs like the rest of your JavaScript code, and that whole `console.log` can be removed. The inlining currently only works for **string, float and boolean**. ## Tips & Tricks This is **not** an optimization. This is an edge-case feature for folks who absolutely need particular values inlined for a JavaScript post-processing step, like conditional compilation. Beside the difference in code that the conditional compilation might end up outputting, there's no performance difference between inlining and not inlining simple values in the eyes of a JavaScript engine. # Installation ## Notes With the instructions below, our new standard library [ReScript Core](https://github.com/rescript-lang/rescript-core) will be included by default. (In ReScript 11, it comes as a separate npm package `@rescript/core`. In future versions, it will be included in the `rescript` npm package itself.) ## Prerequisites - [Node.js](https://nodejs.org/) version >= 14 - One of the following package managers: - [npm](https://docs.npmjs.com/cli/) (comes with Node.js) - [yarn](https://yarnpkg.com/) (yarn versions >1 need to set `nodeLinker: node-modules` in `.yarnrc.yml`) - [pnpm](https://pnpm.io/) - [bun](https://bun.sh/) ## New Project The fastest and easiest way to spin up a new ReScript project is with the [create-rescript-app](https://github.com/rescript-lang/create-rescript-app) project generator. You can start it with any of the aforementioned package managers or `npx`. - Follow the steps of the setup. - Trigger a ReScript build: - If you selected the "basic" template, simply run it with: That compiles your ReScript into JavaScript, then uses Node.js to run said JavaScript. **When taking your first steps with ReScript, we recommend you use our unique workflow of keeping a tab open for the generated JS file** (`.res.js`/`.res.mjs`), so that you can learn how ReScript transforms into JavaScript. Not many languages output clean JavaScript code you can inspect and learn from! With our [VS Code extension](https://marketplace.visualstudio.com/items?itemName=chenglou92.rescript-vscode), use the command "ReScript: Open the compiled JS file for this implementation file" to open the generated JS file for the currently active ReScript source file. During development, instead of running `npm run res:build` each time to compile, use `npm run res:dev` to start a watcher that recompiles automatically after file changes. ## Integrate Into an Existing JS Project If you already have a JavaScript project into which you'd like to add ReScript you can do that in the following ways: ### Quick Setup In the root directory of your project, execute: `create-rescript-app` will tell you that a `package.json` file has been detected and ask you if it should install ReScript into your project. Just follow the steps accordingly. ### Manual Setup - Install ReScript locally: - Create a ReScript build configuration file (called `rescript.json`) at the root: See [Build Configuration](build-configuration) for more details on `rescript.json`. - Add convenience `npm` scripts to `package.json`: Since ReScript compiles to clean readable JS files, the rest of your existing toolchain (e.g. Babel and Webpack) should just work! Helpful guides: - [Converting from JS](/docs/manual/latest/converting-from-js). - [Shared Data Types](shared-data-types). - [Import from/Export to JS](import-from-export-to-js). ### Integrate with a ReactJS Project To start a [rescript-react](/docs/react/latest/introduction) app, or to integrate ReScript into an existing ReactJS app, follow the instructions [here](/docs/react/latest/installation). # Interop Cheatsheet This is a glossary with examples. All the features are described by later pages. ## List of Decorators > **Note:** In ReScript < 8.3, all our attributes started with the `bs.` prefix. This is no longer needed and our formatter automatically removes them in newer ReScript versions. ### Attributes - `@as`: [here](attribute#usage), [here](bind-to-js-function#fixed-arguments), [here](bind-to-js-function#constrain-arguments-better) and [here](generate-converters-accessors#usage-3) - [`@deriving`](generate-converters-accessors#generate-functions--plain-values-for-variants) - [`@get`](bind-to-js-object#bind-using-special-bs-getters--setters) - [`@get_index`](bind-to-js-object#bind-using-special-bs-getters--setters) - [`@inline`](inlining-constants) - [`@int`](bind-to-js-function#constrain-arguments-better) - [`@module`](import-from-export-to-js#import-a-javascript-modules-content) - [`@new`](bind-to-js-object#bind-to-a-js-object-thats-a-class) - [`@optional`](generate-converters-accessors#optional-labels) - [`@return`](bind-to-js-function#function-nullable-return-value-wrapping) - `@send`: [here](bind-to-js-function#object-method) and [here](pipe#js-method-chaining) - [`@scope`](bind-to-global-js-values#global-modules) - [`@set`](bind-to-js-object#bind-using-special-bs-getters--setters) - [`@set_index`](bind-to-js-object#bind-using-special-bs-getters--setters) - [`@variadic`](bind-to-js-function#variadic-function-arguments) - [`@string`](bind-to-js-function#constrain-arguments-better) - [`@this`](bind-to-js-function#modeling-this-based-callbacks) - [`@uncurry`](bind-to-js-function#extra-solution) - [`@unwrap`](bind-to-js-function#trick-2-polymorphic-variant--bsunwrap) - [`@val`](bind-to-global-js-values#global-modules) - [`@taggedTemplate`](bind-to-js-function#tagged_template-functions) - [`@deprecated`](attribute#usage) - [`genType`](https://github.com/reason-association/genType) - [`@JSX`](jsx) - `@react.component`: [here](/docs/react/latest/introduction) and [here](https://github.com/reasonml/reason-react) - [`@warning`](attribute#usage) - [`@unboxed`](variant#untagged-variants) ### Extension Points - [`%debugger`](embed-raw-javascript#debugger) - [`%external`](bind-to-global-js-values#special-global-values) - [`%raw`](embed-raw-javascript#paste-raw-js-code) - [`%re`](primitive-types#regular-expression) - [`%todo`](/syntax-lookup#todo) ## Raw JS ## Global Value ## Global Module's Value ## Nullable Handling a value that can be `undefined` and `null`, by ditching the `option` type and using `Nullable.t`: ## JS Object - [Bind to a JS object as a ReScript record](bind-to-js-object#bind-to-record-like-js-objects). - [Bind to a JS object that acts like a hash map](bind-to-js-object#bind-to-hash-map-like-js-object). - [Bind to a JS object that's a class](bind-to-js-object#bind-to-a-js-object-thats-a-class). ## Function ### Object Method & Chaining ### Variadic Arguments ### Tagged template functions ### Polymorphic Function ## JS Module Interop [See here](import-from-export-to-js.md) ## Dangerous Type Cast Final escape hatch converter. Do not abuse. # Interop with JS Build Systems If you come from JS, chances are that you already have a build system in your existing project. Here's an overview of the role `rescript` would play in your build pipeline, if you want to introduce some ReScript code. > **Please** try not to wrap `rescript` into your own incremental build framework. ReScript's compilation is very hard to get right, and you'll inevitably run into stale or badly performing builds (therefore erasing much of our value proposition) if you create your own meta layer on top. ## Popular JS Build Systems The JS ecosystem uses a few build systems: [browserify](http://browserify.org/), [rollup](https://github.com/rollup/rollup), [webpack](https://webpack.js.org/), etc. The latter's probably the most popular of the three (as of 2019 =P). These build systems do both the compilation and the linking (aka, bundling many files into one or few files). `rescript` only takes care of the compilation step; it maps one `.res`/`.resi` file into one JS output file. As such, in theory, no build system integration is needed from our side. From e.g. the webpack watcher's perspective, the JS files ReScript generates are almost equivalent to your hand-written JS files. We also recommend **that you initially check in those ReScript-generated JS files**, as this workflow means: - You can introduce ReScript silently into your codebase without disturbing existing infra. - You have a **visual** diff of the performance & correctness of your JS file when you update the `.res` files and the JS artifacts change. - You can let teammates hot-patch the JS files in emergency situations, without needing to first start learning ReScript. - You can remove ReScript completely from your codebase and things will still work (in case your company decides to stop using us for whatever reason). For what it's worth, you can also turn `rescript` into an automated step in your build pipeline, e.g. into a Webpack loader; but such approach is error-prone and therefore discouraged. ### Tips & Tricks You can make ReScript JS files look even more idiomatic through the in-source + bs suffix config in `rescript.json`: This will: - Generate the JS files alongside your ReScript source files. - Use the file extension `.res.js`, so that you can require these files on the JS side through `require('./MyFile.res.js')`, without needing a loader. ## Use Loaders on ReScript Side "What if my build system uses a CSS/png/whatever loader and I'd like to use it in ReScript?" Loaders are indeed troublesome; in the meantime, please use e.g. `%raw("require('./myStyles.css')")` at the top of your file. This just uses [`raw`](embed-raw-javascript.md) to compile the snippet into an actual JS require. ## Getting Project's Dependencies `rescript` generates one `MyFile.d` file per `MyFile` source file; you'll find them in `lib/bs`. These are human readable, machine-friendly list of the dependencies of said `MyFile`. You can read into them for your purpose (though mind the IO overhead). Use these files instead of creating your own dependency graph; we did the hard work of tracking the dependencies as best as possible (including inner modules, `open`s, module names overlap, etc). ## Run Script Per File Built See [js-post-build](build-configuration#js-post-build). Though please use it sparingly; if you hook up a node.js script after each file built, you'll incur the node startup time per file! # ReScript Ever wanted a language like JavaScript, but without the warts, with a great type system, and with a lean build toolchain that doesn't waste your time? ReScript looks like JS, acts like JS, and compiles to the highest quality of clean, readable and performant JS, directly runnable in browsers and Node. **This means you can pick up ReScript and access the vast JavaScript ecosystem and tooling as if you've known ReScript for a long time!** **ReScript is the language for folks who don't necessarily love JavaScript, but who still acknowledge its importance**. ## Difference vs TypeScript We respect TypeScript very much and think that it's a positive force in the JavaScript ecosystem. ReScript shares some of the same goals as TypeScript, but is different enough regarding some important nuances: - TypeScript's (admittedly noble) goal is to cover the entire JavaScript feature set and more. **ReScript covers only a curated subset of JavaScript**. For example, we emphasize plain data + functions over classes, clean [pattern matching](pattern-matching-destructuring.md) over fragile `if`s and virtual dispatches, [proper data modeling](variant.md) over string abuse, etc. JavaScript supersets will only grow larger over time; ReScript doesn't. \* - Consequently, TypeScript's type system is necessarily complex, pitfalls-ridden, potentially requires tweaking, sometimes slow, and requires quite a bit of noisy annotations that often feel like manual bookkeeping rather than clear documentation. In contrast, ReScript's type system: - Is deliberately curated to be a simple subset most folks will have an easier time to use. - Has **no** pitfalls, aka the type system is "sound" (the types will always be correct). E.g. If a type isn't marked as nullable, its value will never lie and let through some `undefined` value silently. **ReScript code has no null/undefined errors**. - Is the same for everyone. No knobs, no bikeshedding opportunity. - Runs extremely fast precisely thanks to its simplicity and curation. It's one of the fastest compiler & build system toolchains for JavaScript development. - **Doesn't need type annotations**. Annotate as much or as little as you'd like. The types are inferred by the language (and, again, are guaranteed correct). - Migrating to TypeScript is done "breadth-first," whereas migrating to ReScript is done "depth-first." You can convert your codebase to TypeScript by "turning it on" for all files and annotate here and there; but how much type safety did you gain? How do you measure it? Type errors can still slip in and out of the converted pieces. For ReScript, our interop features draw clear boundaries: there's pure ReScript code, and there's JS interop code. Every piece of converted ReScript code is 100% clean. You'd convert file by file and each conversion increases your safety monotonically. \* When you absolutely need to write or interoperate with free-for-all JavaScript, we expose enough escape hatches for you. ## Other Highlights Aside from the aforementioned simple, robust and fast type system, ReScript presents a few more advantages. ### Faster than JavaScript JavaScript's been aggressively optimized by talented engineers over a long span. Unfortunately, even for seasoned JS devs, it can be hard to know how to properly leverage JS's performance. ReScript's type system and compiler naturally guides you toward writing code that's very often performant by default, with good leverage of various Just-In-Time optimizations (hidden classes, inline caching, avoiding deopts, etc). A widespread adage to write fast JavaScript code is to write as if there's a type system (in order to trigger JS engines' good optimization heuristics); ReScript gives you a real one and generates code that's friendly to optimizations by default. ### High Quality Dead Code Elimination The JavaScript ecosystem is very reliant on dependencies. Shipping the final product inevitably drags in a huge amount of code, lots of which the project doesn't actually use. These regions of dead code impact loading, parsing and interpretation speed. ReScript provides powerful dead code elimination at all levels: - Function- and module-level code elimination is facilitated by the well-engineered type system and purity analysis. - At the global level, ReScript generates code that is naturally friendly to dead code elimination done by bundling tools such as [Rollup](https://github.com/rollup/rollup) and [Closure Compiler](https://developers.google.com/closure/compiler/), after its own sophisticated elimination pass. - The same applies for ReScript's own tiny runtime (which is written in ReScript itself). ### Tiny JS Output A `Hello world` ReScript program generates **20 bytes** of JS code. Additionally, the standard library pieces you require in are only included when needed. ### Fast Iteration Loop ReScript's build time is **one or two orders of magnitude** faster than alternatives. In its watcher mode, the build system usually finishes before you switch screen from the editor to the terminal tab (two digits of milliseconds). A fast iteration cycle reduces the need of keeping one's mental state around longer; this in turn allows one to stay in the flow longer and more often. ### Readable Output & Great Interop Unreadable JavaScript code generated from other compiled-to-js languages makes it so that it could be, practically speaking: - Hard to debug (cryptic stack trace, mangled variable names) - Hard to learn from (non-straightforward mapping of concepts from one language to another) - Hard to profile for performance (unclear what runtime performance cost there is) - Hard to integrate with existing hand-written JS code ReScript's JS output is very readable. This is especially important while learning, where users might want to understand how the code's compiled, and to audit for bugs. This characteristic, combined with a fully-featured JS interop system, allows ReScript code to be inserted into an existing JavaScript codebase almost unnoticed. ### Preservation of Code Structure ReScript maps one source file to one JavaScript output file. This eases the integration of existing tools such as bundlers and test runners. You can even start writing a single file without much change to your build setup. Each file's code structure is approximately preserved, too. ## Conclusion We hope the above gave you enough of an idea of ReScript and its differentiators. Feel free to [try it online](/try) to get a feel! # JSON ## Parse Bind to JavaScript's `JSON.parse` and type the return value as the type you're expecting: Where `data` can be any type you assume the JSON is. As you can see, this compiles to a straightforward `JSON.parse` call. As with regular JS, this is convenient, but has no guarantee that e.g. the data is correctly shaped, or even syntactically valid. Slightly dangerous. ## Stringify Use [`JSON.stringify`](api/core/json#value-stringify) if your data is of type `JSON.t` or [`JSON.stringifyAny`](api/core/json#value-stringifyAny) if it is not. ## Import a JSON file Use the `@module` attribute to import JSON files directly. ## Advanced Thanks to untagged variants, JSON can be encoded and decoded idiomatically. Check it out on [the variants page](variant#decoding-and-encoding-json-idiomatically). # JSX Would you like some HTML syntax in your ReScript? If not, quickly skip over this section and pretend you didn't see anything! ReScript supports the JSX syntax, with some slight differences compared to the one in [ReactJS](https://facebook.github.io/react/docs/introducing-jsx.html). ReScript JSX isn't tied to ReactJS; they translate to normal function calls: **Note** for [ReScriptReact](https://rescript-lang.org/docs/react/latest/introduction) readers: this isn't what ReScriptReact turns JSX into, in the end. See Usage section for more info. ## Capitalized becomes ## Uncapitalized becomes ## Fragment becomes ### Children This is the syntax for passing a list of two items, `child1` and `child2`, to the children position. It transforms to a list containing `child1` and `child2`: **Note** again that this isn't the transform for ReScriptReact; ReScriptReact turns the final list into an array. But the idea still applies. So naturally, ` myChild ` is transformed to `MyComponent.createElement(~children=list{myChild}, ())`. I.e. whatever you do, the arguments passed to the children position will be wrapped in a list. ## Usage See [ReScriptReact Elements & JSX](https://rescript-lang.org/docs/react/latest/elements-and-jsx) for an example application of JSX, which transforms the above calls into a ReScriptReact-specific call. Here's a JSX tag that shows most of the features. ## Departures From JS JSX - Attributes and children don't mandate `{}`, but we show them anyway for ease of learning. Once you format your file, some of them go away and some turn into parentheses. - Props spread is supported, but there are some restrictions (see below). - Punning! - Props and tag names have to follow ReScript's restrictions on identifiers at the exception of hyphens for lowercase tags ([see below](#hyphens-in-tag-names)). ### Spread Props **Since 10.1** JSX props spread is supported now, but in a stricter way than in JS. Multiple spreads are not allowed: The spread must be at the first position, followed by other props: ### Punning "Punning" refers to the syntax shorthand for when a label and a value are the same. For example, in JavaScript, instead of doing `return {name: name}`, you can do `return {name}`. JSX supports punning. `` is just a shorthand for ``. The formatter will help you format to the punned syntax whenever possible. This is convenient in the cases where there are lots of props to pass down: Consequently, a JSX component can cram in a few more props before reaching for extra libraries solutions that avoids props passing. **Note** that this is a departure from ReactJS JSX, which does **not** have punning. ReactJS' `` desugars to ``, in order to conform to DOM's idioms and for backward compatibility. ### Hyphens in tag names **Since 11.1** JSX now supports lowercase tags with hyphens in their name. This allows to bind to web components. Note though that props names can't have hyphens, you should use `@as` to bind to such props in your custom `JsxDOM.domProps` type ([see generic JSX transform](#generic-jsx-transform-jsx-beyond-react-experimental)). ## Generic JSX transform: JSX beyond React (experimental) **Since 11.1** While ReScript comes with first class support for JSX in React, it's also possible to have ReScript delegate JSX to other frameworks. You do that by configuring a _generic JSX transform_. This is what you need to do to use a generic JSX transform: 1. Make sure you have a ReScript module that [implements the functions and types necessary for the JSX transform](#implementing-a-generic-jsx-transform-module). 2. Configure `rescript.json` to delegated JSX to that module. That's it really. We'll expand on each point below. ### Configuration You configure a generic JSX transform by putting any module name in the `module` config of JSX in `rescript.json`. This can be _any valid module name_. Example part from `rescript.json`: This will now put the `Preact` module in control of the generated JSX calls. The `Preact` module can be defined by anyone - locally in your project, or by a package. As long a it's available in the global scope. The JSX transform will delegate any JSX related code to `Preact`. #### What about `@react.component` for components? `@react.component` will still be available, and so is a generic `@jsx.component` notation. Both work the same way. ### Usage Example Here's a quick usage example (the actual definition of `Preact.res` comes below): First, configure `rescript.json`: Now you can build Preact components: And you can use them just like normal with JSX: #### File level configuration You can configure what JSX transform is used at the file level via `@@jsxConfig`, just like before. Like: This can be convenient if you're mixing different JSX frameworks in the same project. ### Implementing a generic JSX transform module Below is a full list of everything you need in a generic JSX transform module, including code comments to clarify. It's an example implementation of a `Preact` transform, so when doing this for other frameworks you'd of course adapt what you import from, and so on. > You can easily copy-paste-and-adapt this to your needs if you're creating bindings to a JSX framework. Most often, all you'll need to change is what the `@module("") external` points to, so the runtime calls point to the correct JS module. As you can see, most of the things you'll want to implement will be copy paste from the above. But do note that **everything needs to be there unless explicitly noted** or the transform will fail at compile time.# Lazy Value If you have some expensive computations you'd like to **defer and cache** subsequently, you can wrap it with `lazy`: Check the JS Output tab: that `expensiveFilesRead`'s code isn't executed yet, even though you declared it! You can carry it around without fearing that it'll run the directory read. **Note**: a lazy value is **not** a [shared data type](shared-data-types.md). Don't rely on its runtime representation in your JavaScript code. ## Execute The Lazy Computation To actually run the lazy value's computation, use `Lazy.force` from the globally available `Lazy` module: The first time `Lazy.force` is called, the expensive computation happens and the result is **cached**. The second time, the cached value is directly used. **You can't re-trigger the computation after the first `force` call**. Make sure you only use a lazy value with computations whose results don't change (e.g. an expensive server request whose response is always the same). Instead of using `Lazy.force`, you can also use [pattern matching](pattern-matching-destructuring.md) to trigger the computation: Since pattern matching also works on a `let` binding, you can also do: ## Exception Handling For completeness' sake, our files read example might raise an exception because of `readdirSync`. Here's how you'd handle it: Though you should probably handle the exception inside the lazy computation itself. # Let Binding A "let binding", in other languages, might be called a "variable declaration". `let` _binds_ values to names. They can be seen and referenced by code that comes _after_ them. ## Block Scope Bindings can be scoped through `{}`. The value of the last line of a scope is implicitly returned. ### Design Decisions ReScript's `if`, `while` and functions all use the same block scoping mechanism. The code below works **not** because of some special "if scope"; but simply because it's the same scope syntax and feature you just saw: ## Bindings Are Immutable Let bindings are "immutable", aka "cannot change". This helps our type system deduce and optimize much more than other languages (and in turn, help you more). ## Binding Shadowing The above restriction might sound unpractical at first. How would you change a value then? Usually, 2 ways: The first is to realize that many times, what you want isn't to mutate a variable's value. For example, this JavaScript pattern: ...is really just to comment on intermediate steps. You didn't need to mutate `result` at all! You could have just written this JS: In ReScript, this obviously works too: Additionally, reusing the same let binding name overshadows the previous bindings with the same name. So you can write this too: (Though for the sake of clarity, we don't recommend this). As a matter of fact, even this is valid code: The binding you refer to is whatever's the closest upward. No mutation here! If you need _real_ mutation, e.g. passing a value around, have it modified by many pieces of code, we provide a slightly heavier [mutation feature](mutation.md). ## Private let bindings Private let bindings are introduced in the release [7.2](https://rescript-lang.org/blog/bucklescript-release-7-2). In the module system, everything is public by default, the only way to hide some values is by providing a separate signature to list public fields and their types: `%%private` gives you an option to mark private fields directly `%%private` also applies to file level modules, so in some cases, users do not need to provide a separate interface file just to hide some particular values. Note interface files are still recommended as a general best practice since they give you better separate compilation units and also they're better for documentation. Still, `%%private` is useful in the following scenarios: - **Code generators.** Some code generators want to hide some values but it is sometimes very hard or time consuming for code generators to synthesize the types for public fields. - **Quick prototyping.** During prototyping, we still want to hide some values, but the interface file is not stable yet. `%%private` provides you such convenience. # Libraries & Publishing ReScript libraries are just like JavaScript libraries: published & hosted on [NPM](http://npmjs.com). You can reuse your `npm`, `yarn` and `package.json`-related tools to manage them! ## Tips & Tricks ### Publish We recommend you to check in your compiled JavaScript output, for its [various benefits](interop-with-js-build-systems.md#popular-js-build-systems). If not, then at least consider publishing the JavaScript output by un-ignoring them in your [npmignore](https://docs.npmjs.com/cli/v7/using-npm/developers#keeping-files-out-of-your-package). This way, your published ReScript package comes with plain JavaScript files that JS users can consume. If your project's good, JS users might not even realize that they've installed a library written in ReScript! In case your library is only consumed by JS users, you may want to check out our [external stdlib](./build-external-stdlib) configuration as well. ### Find Libraries Search `rescript`-related packages on NPM, or use our [Package Index](/packages). If you can't find what you're looking for, remember that **you don't need a wrapper** to use a JS library: - Most JS data types, such as array and objects, [map over cleanly to ReScript and vice-versa](shared-data-types.md). - You also have access to the familiar [Core API](api/core). - You can use a JavaScript library without needing to install dedicated binding libraries. Check the [`external`](external) page. # Documentation for LLMs We adhere to the [llms.txt convention](https://llmstxt.org/) to make documentation accessible to large language models and their applications. Currently, we have the following files... - [/docs/manual/llms.txt](/llms/manual/v11.0.0/llms.txt) — a list of the available files for ReScript language. - [/docs/manual/llm-full.txt](/llms/manual/v11.0.0/llm-full.txt) — complete documentation for ReScript language. - [/docs/manual/llm-small.txt](/llms/manual/v11.0.0/llm-small.txt) — compressed version of the former, without examples. ...and package-level documentation: - [/docs/react/llms](/docs/react/latest/llms) — the LLms documentation for ReScript React. ## Notes - The content is automatically generated from the same source as the official documentation for the specific version # Migrate to ReScript 11 ## Foreword The ReScript community is proud to introduce ReScript V11 which comes with a ton of new features but also removes a lot of bulk. A migration to it can be very straightforward, but it can also take some time, depending on your code style or what dependencies you use. Please have a look at the full [set of breaking changes](#list-of-all-breaking-changes) below to be able to decide whether this is a task you want to undertake. There is also the possibilty to [opt-out of uncurried mode](#minimal-migration) for now, which is probably the most fundamental change of this release. That and other new and notable features are discussed in the following blogposts: - [Better interop with customizable variants](/blog/improving-interop) - [Enhanced Ergonomics for Record Types](/blog/enhanced-ergonomics-for-record-types) - [First-class Dynamic Import Support](/blog/first-class-dynamic-import-support) - [Uncurried Mode](/blog/uncurried-mode) ## Recommended Migration ### Uncurried Mode For uncurried mode to take effect in ReScript 11 there is nothing to configure, it is activated by default. ### Adapt suffix ReScript 11 now allows having arbitrary suffixes in the generated JavaScript files. However, it is still recommended to stick to using `.res.js`, `.res.mjs` or `.res.cjs`. For more information, read the Build System Configuration about [suffixes](/docs/manual/latest/build-configuration#suffix). ### rescript.json The old configuration filename `bsconfig.json` is deprecated. Rename `bsconfig.json` to `rescript.json` to get rid of the deprecation warning. ### ReScript Core standard library [ReScript Core](https://github.com/rescript-lang/rescript-core) is ReScript's new standard library. It replaces the complete `Js` module as well as some of the more frequently used modules from `Belt` and is recommended to use with uncurried mode. It will be integrated into the compiler in a future version. In ReScript 11, it still needs to be installed manually: Then add `@rescript/core` to your `rescript.json`'s dependencies: Open it so it's available in the global scope. One major change to be aware of is that array access now returns an `option`. If you would like to not use an `option`, you can use [`Array.getUnsafe`](api/core/array#value-getUnsafe). For a detailed explanation on migration to ReScript Core, please refer to its [migration guide](https://github.com/rescript-lang/rescript-core#migration). A semi-automated script is available as well. See ReScript Core API docs [here](api/core). ### Removed bindings Many Node bindings have been removed from the compiler. Please use [rescript-nodejs](https://github.com/TheSpyder/rescript-nodejs) instead or write your own local bindings. ## Minimal Migration This guide describes the things to do at least to migrate to ReScript 11. ### Disable uncurried mode If you use currying extensively and don't want to bother with adapting your code, or have dependencies that just don't work with uncurried mode yet, just set it to false in your `rescript.json`. For more information, read the Build System Configuration about [uncurried](/docs/manual/latest/build-configuration#uncurried). ## List of all breaking changes Below is an excerpt from the compiler changelog about all the breaking changes of ReScript 11. ### Language and Compiler - Add smart printer for pipe chains. https://github.com/rescript-lang/rescript/pull/6411 (the formatter will reformat existing code in certain cases) - Parse `assert` as a regular function. `assert` is no longer a unary expression. Example: before `assert 1 == 2` is parsed as `(assert 1) == 2`, now it is parsed as `assert(1 == 2)`. https://github.com/rescript-lang/rescript/pull/6180 - Remove support for the legacy Reason syntax. Existing Reason code can be converted to ReScript syntax using ReScript 9 as follows: - `npx rescript@9 convert ` - Curried after uncurried is not fused anymore: `(. x) => y => 3` is not equivalent to `(. x, y) => 3` anymore. It's instead equivalent to `(. x) => { y => 3 }`. Also, `(. int) => string => bool` is not equivalen to `(. int, string) => bool` anymore. These are only breaking changes for unformatted code. - Exponentiation operator `**` is now right-associative. `2. ** 3. ** 2.` now compile to `Math.pow(2, Math.pow(3, 2))` and not anymore `Math.pow(Math.pow(2, 3), 2)`. Parentheses can be used to change precedence. - Stop mangling object field names. If you had objects with field names containing "\__" or leading "_", they won't be mangled in the compiled JavaScript and represented as it is without changes. https://github.com/rescript-lang/rescript/pull/6354 - `$$default` is no longer exported from the generated JavaScript when using default exports. https://github.com/rescript-lang/rescript/pull/6328 - `-bs-super-errors` flag has been deprecated along with Super_errors. https://github.com/rescript-lang/rescript/pull/6243 - Remove unsafe `` j`$(a)$(b)` `` interpolation deprecated in compiler version 10 https://github.com/rescript-lang/rescript/pull/6068 - `@deriving(jsConverter)` not supported anymore for variant types https://github.com/rescript-lang/rescript/pull/6088 - New representation for variants, where the tag is a string instead of a number. https://github.com/rescript-lang/rescript/pull/6088 ### Compiler Libraries - Fixed name collision between the newly defined Js.Json.t and the variant constructor in the existing Js.Json.kind type. To address this, the usage of the existing Js.Json.kind type can be updated to Js.Json.Kind.t. https://github.com/rescript-lang/rescript/pull/6317 - Remove rudimentary node bindings and undocumented `%node` extension. https://github.com/rescript-lang/rescript/pull/6285 - `@rescript/react` >= 0.12.0-alpha.2 is now required because of the React.fragment's children type fix. https://github.com/rescript-lang/rescript/pull/6238 - Remove deprecated module `Printexc` ### Build System and Tools - Update watcher rules to recompile only on config and `*.res`/`*.resi`/`*.ml`/`.mli` file changes. Solves the issue of unnecessary recompiles on `.css`, `.ts`, and other unrelated file changes. https://github.com/rescript-lang/rescript/pull/6420 - Made pinned dependencies transitive: if _a_ is a pinned dependency of _b_ and _b_ is a pinned dependency of _c_, then _a_ is implicitly a pinned dependency of _c_. This change is only breaking if your build process assumes non-transitivity. - Remove obsolete built-in project templates and the "rescript init" functionality. This is replaced by [create-rescript-app](https://github.com/rescript-lang/create-rescript-app) which is maintained separately. - Do not attempt to build ReScript from source on npm postinstall for platforms without prebuilt binaries anymore. - GenType: removed support for `@genType.as` for records and variants which has become unnecessary. Use the language's `@as` instead to channge the runtime representation without requiring any runtime conversion during FFI. https://github.com/rescript-lang/rescript/pull/6099 https://github.com/rescript-lang/rescript/pull/6101 # Module Functions Module functions can be used to create modules based on types, values, or functions from other modules. This is a powerful tool that can be used to create abstractions and reusable code that might not be possible with functions, or might have a runtime cost if done with functions. This is an advanced part of ReScript and you can generally get by with normal values and functions. ## Quick example Next.js has a `useParams` hook that returns an unknown type, and it's up to the developer in TypeScript to add a type annotation for the parameters returned by the hook. In ReScript we can create a module function that will return a typed response for the `useParams` hook. Now the `PersonData` module has the functions from the `MakeDataModule`. ## Dependency injection Module functions can be used for dependency injection. Here's an example of injecting in some config values into a set of functions to access a database. # 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. A module's contents (including types!) can be accessed much like a record's, using the `.` notation. This demonstrates modules' utility for namespacing. Nested modules work too. ### `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: We can write: 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: ### 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. **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). **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! **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. 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: 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**: **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. ### 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. ## 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. Module functions can be applied using function application syntax. In this case, we're creating a set, whose items are pairs of integers. ### 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! ## 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. # Mutation ReScript has great traditional imperative & mutative programming capabilities. You should use these features sparingly, but sometimes they allow your code to be more performant and written in a more familiar pattern. ## Mutate Let-binding Let-bindings are immutable, but you can wrap it with a `ref`, exposed as a record with a single mutable field in the standard library: ## Usage You can get the actual value of a `ref` box through accessing its `contents` field: Assign a new value to `myValue` like so: We provide a syntax sugar for this: Note that the previous binding `five` stays `5`, since it got the underlying item on the `ref` box, not the `ref` itself. **Note**: you might see in the JS output tabs above that `ref` allocates an object. Worry not; local, non-exported `ref`s allocations are optimized away. ## Tip & Tricks Before reaching for `ref`, know that you can achieve lightweight, local "mutations" through [overriding let bindings](let-binding.md#binding-shadowing). # Newcomer Examples An example is worth a thousand words. This section is dedicated to newcomers trying to figure out general idioms & conventions. If you're a beginner who's got a good idea for an example, please suggest an edit! ## Use the [`option` type](null-undefined-option.md) ## Create a Parametrized Type ## Creating a JS Object Or using [record](record.md): ## Modeling a JS Module with Default Export See [here](import-from-export-to-js.md#import-a-javascript-module-itself-es6-module-format). ## Checking for JS nullable types using the `option` type For a function whose argument is passed a JavaScript value that's potentially `null` or `undefined`, it's idiomatic to convert it to an `option`. The conversion is done through the helper functions in ReScript's [`Nullable`](api/core/nullable#value-toOption) module. In this case, `toOption`: This check compiles to `possiblyNullName == null` in JS, so checks for the presence of `null` or `undefined`. # Null, Undefined and Option ReScript itself doesn't have the notion of `null` or `undefined`. This is a _great_ thing, as it wipes out an entire category of bugs. No more `undefined is not a function`, and `cannot access someAttribute of undefined`! However, the **concept** of a potentially nonexistent value is still useful, and safely exists in our language. We represent the existence and nonexistence of a value by wrapping it with the `option` type. Here's its definition from the standard library: It means "a value of type option is either None (representing nothing) or that actual value wrapped in a Some". **Note** how the `option` type is just a regular [variant](variant.md). ## Example Here's a normal value: To represent the concept of "maybe null", you'd turn this into an `option` type by wrapping it. For the sake of a more illustrative example, we'll put a condition around it: Later on, when another piece of code receives such value, it'd be forced to handle both cases through [pattern matching](pattern-matching-destructuring.md): By turning your ordinary number into an `option` type, and by forcing you to handle the `None` case, the language effectively removed the possibility for you to mishandle, or forget to handle, a conceptual `null` value! **A pure ReScript program doesn't have null errors**. ## Interoperate with JavaScript `undefined` and `null` The `option` type is common enough that we special-case it when compiling to JavaScript: simply compiles down to `5`, and compiles to `undefined`! If you've got e.g. a string in JavaScript that you know might be `undefined`, type it as `option` and you're done! Likewise, you can send a `Some(5)` or `None` to the JS side and expect it to be interpreted correctly =) ### Caveat 1 The option-to-undefined translation isn't perfect, because on our side, `option` values can be composed: This still compiles to `5`, but this gets troublesome: What's this `Caml_option.some` thing? Why can't this compile to `undefined`? Long story short, when dealing with a polymorphic `option` type (aka `option<'a>`, for any `'a`), many operations become tricky if we don't mark the value with some special annotation. If this doesn't make sense, don't worry; just remember the following rule: - **Never, EVER, pass a nested `option` value (e.g. `Some(Some(Some(5)))`) into the JS side.** - **Never, EVER, annotate a value coming from JS as `option<'a>`. Always give the concrete, non-polymorphic type.** ### Caveat 2 Unfortunately, lots of times, your JavaScript value might be _both_ `null` or `undefined`. In that case, you unfortunately can't type such value as e.g. `option`, since our `option` type only checks for `undefined` and not `null` when dealing with a `None`. #### Solution: More Sophisticated `undefined` & `null` Interop To solve this, we provide access to more elaborate `null` and `undefined` helpers through the [`Nullable`](api/core/nullable) module. This somewhat works like an `option` type, but is different from it. #### Examples To create a JS `null`, use the value `Nullable.null`. To create a JS `undefined`, use `Nullable.undefined` (you can naturally use `None` too, but that's not the point here; the `Nullable.*` helpers wouldn't work with it). If you're receiving, for example, a JS string that can be `null` and `undefined`, type it as: To create such a nullable string from our side (presumably to pass it to the JS side, for interop purpose), do: The `return` part "wraps" a string into a nullable string, to make the type system understand and track the fact that, as you pass this value around, it's not just a string, but a string that can be `null` or `undefined`. #### Convert to/from `option` `Nullable.fromOption` converts from a `option` to `Nullable.t`. `Nullable.toOption` does the opposite. # Object ReScript objects are like [records](record.md), but: - No type declaration needed. - Structural and more polymorphic, [unlike records](record.md#record-types-are-found-by-field-name). - Doesn't support updates unless the object comes from the JS side. - Doesn't support [pattern matching](pattern-matching-destructuring). Although ReScript records compile to clean JavaScript objects, ReScript objects are a better candidate for emulating/binding to JS objects, as you'll see. ## Type Declaration **Optional**, unlike for records. The type of an object is inferred from the value, so you never really need to write down its type definition. Nevertheless, here's its type declaration syntax: Visually similar to record type's syntax, with the field names quoted. ## Creation To create a new object: **Note**: as said above, unlike for record, this `me` value does **not** try to find a conforming type declaration with the field `"age"` and `"name"`; rather, the type of `me` is inferred as `{"age": int, "name": string}`. This is convenient, but also means this code passes type checking without errors: Since the type checker doesn't try to match `me` with the type `person`. If you ever want to force an object value to be of a predeclared object type, just annotate the value: Now the type system will error properly. ## Access ## Update Disallowed unless the object is a binding that comes from the JavaScript side. In that case, use `=` ## Combine Types You can spread one object type definition into another using `...`: This only works with object types, not object values! ## Tips & Tricks Since objects don't require type declarations, and since ReScript infers all the types for you, you get to very quickly and easily (and dangerously) bind to any JavaScript API. Check the JS output tab: The `external` feature and the usage of this trick are also documented in the [external](external#tips--tricks) section later. It's an excellent way to start writing some ReScript code without worrying about whether bindings to a particular library exists. # Overview ## Comparison to JS ### Semicolon | JavaScript | ReScript | | ---------------------------------- | -------------------- | | Rules enforced by linter/formatter | No semicolon needed! | ### Comments | JavaScript | ReScript | | -------------------- | -------------------------------- | | `// Line comment` | Same | | `/* Comment */` | Same | | `/** Doc Comment */` | `/** Before Types/Values */` | | | `/*** Standalone Doc Comment */` | ### Variable | JavaScript | ReScript | | ----------------------- | ------------------------------------- | | `const x = 5;` | `let x = 5` | | `var x = y;` | No equivalent (thankfully) | | `let x = 5; x = x + 1;` | `let x = ref(5); x := x.contents + 1` | ### String & Character | JavaScript | ReScript | | ---------------------------- | --------------------- | | `"Hello world!"` | Same | | `'Hello world!'` | Strings must use `"` | | `"hello " + "world"` | `"hello " ++ "world"` | | `` `hello ${message}` `` | Same | | `` sql`select ${fnName};` `` | Same | ### Boolean | JavaScript | ReScript | | ----------------------------------------------------- | ---------------------------------------------- | | `true`, `false` | Same | | `!true` | Same | | ||, `&&`, `<=`, `>=`, `<`, `>` | Same | | `a === b`, `a !== b` | Same | | No deep equality (recursive compare) | `a == b`, `a != b` | | `a == b` | No equality with implicit casting (thankfully) | ### Number | JavaScript | ReScript | | ----------- | ------------ | | `3` | Same \* | | `3.1415` | Same | | `3 + 4` | Same | | `3.0 + 4.5` | `3.0 +. 4.5` | | `5 % 3` | `mod(5, 3)` | \* JS has no distinction between integer and float. ### Object/Record | JavaScript | ReScript | | ------------------- | --------------------------------------- | | no types | `type point = {x: int, mutable y: int}` | | `{x: 30, y: 20}` | Same | | `point.x` | Same | | `point.y = 30;` | Same | | `{...point, x: 30}` | Same | ### Array | JavaScript | ReScript | | ------------------ | --------------------- | | `[1, 2, 3]` | Same | | `myArray[1] = 10` | Same | | `[1, "Bob", true]` | `(1, "Bob", true)` \* | \* ReScript does not have heterogenous arrays. Use tuples or [Untagged Variants](variant#untagged-variants) instead. ### Null | JavaScript | ReScript | | ------------------- | --------- | | `null`, `undefined` | `None` \* | \* Again, only a spiritual equivalent; we don't have nulls, nor null bugs! But we do have an `option` type for when you actually need nullability. ### Function | JavaScript | ReScript | | ------------------------------- | ---------------------------- | | `arg => retVal` | Same | | `function named(arg) {...}` | `let named = (arg) => {...}` | | `const f = function(arg) {...}` | `let f = (arg) => {...}` | | `add(4, add(5, 6))` | Same | ### Async Function / Await | JavaScript | ReScript | | --------------------------------------- | ----------------------------------------------------- | | `async (arg) => {...}` | Same | | `async function named(arg) {...}` | `let named = async (arg) => {...}` | | `await somePromise` | Same | | `async (arg): Promise => {...}` | `async (arg): string => {...}` (note the return type) | ### Blocks
JavaScript ReScript
``` const myFun = (x, y) => { const doubleX = x + x; const doubleY = y + y; return doubleX + doubleY; }; ``` ``` let myFun = (x, y) => { let doubleX = x + x let doubleY = y + y doubleX + doubleY } ```
### If-else | JavaScript | ReScript | | --------------------- | --------------------------------------------------------------------------------- | | `if (a) {b} else {c}` | `if a {b} else {c}` \* | | `a ? b : c` | Same | | `switch` | `switch` but [super-powered pattern matching!](pattern-matching-destructuring.md) | \* Our conditionals are always expressions! You can write `let result = if a {"hello"} else {"bye"}` ### Destructuring | JavaScript | ReScript | | ----------------------------- | --------------------------- | | `const {a, b} = data` | `let {a, b} = data` | | `const [a, b] = data` | `let [a, b] = data` \* | | `const {a: aa, b: bb} = data` | `let {a: aa, b: bb} = data` | \* Gives good compiler warning that `data` might not be of length 2. ### Loop | JavaScript | ReScript | | ------------------------------------- | ---------------------------- | | `for (let i = 0; i <= 10; i++) {...}` | `for i in 0 to 10 {...}` | | `for (let i = 10; i >= 0; i--) {...}` | `for i in 10 downto 0 {...}` | | `while (true) {...}` | `while true {...}` | ### JSX | JavaScript | ReScript | | ----------------------------------------- | -------------------------- | | `` | Same | | `` | `` \* | | `` | `` | | No children spread | `...children` | \* Argument punning! ### Exception | JavaScript | ReScript | | ----------------------------------------- | -------------------------------------------- | | `throw new SomeError(...)` | `raise(SomeError(...))` | | `try {a} catch (err) {...} finally {...}` | `try a catch { \| SomeError(err) => ...}` \* | \* No finally. ### Blocks The last expression of a block delimited by `{}` implicitly returns (including function body). In JavaScript, this can only be simulated via an immediately-invoked function expression (since function bodies have their own local scope).
JavaScript ReScript
``` let result = (function() { const x = 23; const y = 34; return x + y; })(); ``` ``` let result = { let x = 23 let y = 34 x + y } ```
## Common Features' JS Output | Feature | Example | JavaScript Output | | ------------------------------- | ------------------------------------ | ------------------------------------------ | | String | `"Hello"` | `"Hello"` | | String Interpolation | `` `Hello ${message}` `` | `"Hello " + message` | | Character (disrecommended) | `'x'` | `120` (char code) | | Integer | `23`, `-23` | `23`, `-23` | | Float | `23.0`, `-23.0` | `23.0`, `-23.0` | | Integer Addition | `23 + 1` | `23 + 1` | | Float Addition | `23.0 +. 1.0` | `23.0 + 1.0` | | Integer Division/Multiplication | `2 / 23 * 1` | `2 / 23 * 1` | | Float Division/Multiplication | `2.0 /. 23.0 *. 1.0` | `2.0 / 23.0 * 1.0` | | Float Exponentiation | `2.0 ** 3.0` | `Math.pow(2.0, 3.0)` | | String Concatenation | `"Hello " ++ "World"` | `"Hello " + "World"` | | Comparison | `>`, `<`, `>=`, `<=` | `>`, `<`, `>=`, `<=` | | Boolean operation | `!`, `&&`, || | `!`, `&&`, || | | Shallow and deep Equality | `===`, `==` | `===`, `==` | | List (disrecommended) | `list{1, 2, 3}` | `{hd: 1, tl: {hd: 2, tl: {hd: 3, tl: 0}}}` | | List Prepend | `list{a1, a2, ...oldList}` | `{hd: a1, tl: {hd: a2, tl: theRest}}` | | Array | `[1, 2, 3]` | `[1, 2, 3]` | | Record | `type t = {b: int}; let a = {b: 10}` | `var a = {b: 10}` | | Multiline Comment | `/* Comment here */` | Not in output | | Single line Comment | `// Comment here` | Not in output | _Note that this is a cleaned-up comparison table; a few examples' JavaScript output are slightly different in reality._ # Pattern Matching / Destructuring One of ReScript's **best** feature is our pattern matching. Pattern matching combines 3 brilliant features into one: - Destructuring. - `switch` based on shape of data. - Exhaustiveness check. We'll dive into each aspect below. ## Destructuring Even JavaScript has destructuring, which is "opening up" a data structure to extract the parts we want and assign variable names to them: Destructuring works with most built-in data structures: You can also use destructuring anywhere you'd usually put a binding: For a record, you can rename the field while destructuring: You _can_ in theory destructure array and list at the top level too: But the array example is **highly disrecommended** (use tuple instead) and the list example will error on you. They're only there for completeness' sake. As you'll see below, the proper way of using destructuring array and list is using `switch`. ## `switch` Based on Shape of Data While the destructuring aspect of pattern matching is nice, it doesn't really change the way you think about structuring your code. One paradigm-changing way of thinking about your code is to execute some code based on the shape of the data. Consider a variant: We'd like to handle each of the 3 cases differently. For example, print a success message if the value is `GoodResult(...)`, do something else when the value is `NoResult`, etc. In other languages, you'd end up with a series of if-elses that are hard to read and error-prone. In ReScript, you can instead use the supercharged `switch` pattern matching facility to destructure the value while calling the right code based on what you destructured: In this case, `message` will have the value `"Success! Product shipped!"`. Suddenly, your if-elses that messily checks some structure of the value got turned into a clean, compiler-verified, linear list of code to execute based on exactly the shape of the value. ### Complex Examples Here's a real-world scenario that'd be a headache to code in other languages. Given this data structure: Imagine this requirement: - Informally greet a person who's a teacher and if his name is Mary or Joe. - Greet other teachers formally. - If the person's a student, congratulate him/her score if they passed the semester. - If the student has a gpa of 0 and is on vacations or sabbatical, display a different message. - A catch-all message for a student. ReScript can do this easily! **Note** how we've: - drilled deep down into the value concisely - using a **nested pattern check** `"Mary" | "Joe"` and `Vacations | Sabbatical` - while extracting the `daysLeft` number from the latter case - and assigned the greeting to the binding `message`. Here's another example of pattern matching, this time on an inline tuple. **Note** how pattern matching on a tuple is equivalent to a 2D table: isBig \ myAnimal | Dog | Cat | Bird -----------------|-----|-----|------ true | 1 | 2 | 3 false | 4 | 4 | 5 ### Fall-Through Patterns The nested pattern check, demonstrated in the earlier `person` example, also works at the top level of a `switch`: Having multiple cases fall into the same handling can clean up certain types of logic. ### Ignore Part of a Value If you have a value like `Teacher(payload)` where you just want to pattern match on the `Teacher` part and ignore the `payload` completely, you can use the `_` wildcard like this: `_` also works at the top level of the `switch`, serving as a catch-all condition: **Do not** abuse a top-level catch-all condition. Instead, prefer writing out all the cases: Slightly more verbose, but a one-time writing effort. This helps when you add a new variant case e.g. `Quarantined` to the `status` type and need to update the places that pattern match on it. A top-level wildcard here would have accidentally and silently continued working, potentially causing bugs. ### If Clause Sometime, you want to check more than the shape of a value. You want to also run some arbitrary check on it. You might be tempted to write this: `switch` patterns support a shortcut for the arbitrary `if` check, to keep your pattern linear-looking: **Note:** Rescript versions < 9.0 had a `when` clause, not an `if` clause.  Rescript 9.0 changed `when` to `if`.  (`when` may still work, but is deprecated.) ### Match on Exceptions If the function throws an exception (covered later), you can also match on _that_, in addition to the function's normally returned values. ### Match on Array ### Match on List Pattern matching on list is similar to array, but with the extra feature of extracting the tail of a list (all elements except the first one): ### Small Pitfall **Note**: you can only pass literals (i.e. concrete values) as a pattern, not let-binding names or other things. The following doesn't work as expected: A first time ReScript user might accidentally write that code, assuming that it's matching on `coordinates` when the second value is of the same value as `centerY`. In reality, this is interpreted as matching on coordinates and assigning the second value of the tuple to the name `centerY`, which isn't what's intended. ## Exhaustiveness Check As if the above features aren't enough, ReScript also provides arguably the most important pattern matching feature: **compile-time check of missing patterns**. Let's revisit one of the above examples: Did you see what we removed? This time, we've omitted the handling of the case where `person1` is `Teacher({name})` when `name` isn't Mary or Joe. Failing to handle every scenario of a value likely constitutes the majority of program bugs out there. This happens very often when you refactor a piece of code someone else wrote. Fortunately for ReScript, the compiler will tell you so: ``` Warning 8: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: Some({name: ""}) ``` **BAM**! You've just erased an entire category of important bugs before you even ran the code. In fact, this is how most of nullable values is handled: If you don't handle the `None` case, the compiler warns. No more `undefined` bugs in your code! ## Conclusion & Tips & Tricks Hopefully you can see how pattern matching is a game changer for writing correct code, through the concise destructuring syntax, the proper conditions handling of `switch`, and the static exhaustiveness check. Below is some advice: Avoid using the wildcard `_` unnecessarily. Using the wildcard `_` will bypass the compiler's exhaustiveness check. Consequently, the compiler will not be able to notify you of probable errors when you add a new case to a variant. Try only using `_` against infinite possibilities, e.g. string, int, etc. Use the `if` clause sparingly. **Flatten your pattern-match whenever you can**. This is a real bug remover. Here's a series of examples, from worst to best: Now that's just silly =). Let's turn it into pattern-matching: Slightly better, but still nested. Pattern-matching allows you to do this: Much more linear-looking! Now, you might be tempted to do this: Which is much more concise, but kills the exhaustiveness check mentioned above; refrain from using that. This is the best: Pretty darn hard to make a mistake in this code at this point! Whenever you'd like to use an if-else with many branches, prefer pattern matching instead. It's more concise and [performant](variant#design-decisions) too. # Pipe ReScript provides a tiny but surprisingly useful operator `->`, called the "pipe", that allows you to "flip" your code inside-out. `a(b)` becomes `b->a`. It's a simple piece of syntax that doesn't have any runtime cost. Why would you use it? Imagine you have the following: This is slightly hard to read, since you need to read the code from the innermost part, to the outer parts. Use pipe to streamline it: Basically, `parseData(person)` is transformed into `person->parseData`, and `getAge(person->parseData)` is transformed into `person->parseData->getAge`, etc. **This works when the function takes more than one argument too**. is the same as This also works with labeled arguments. Pipes are used to emulate object-oriented programming. For example, `myStudent.getName` in other languages like Java would be `myStudent->getName` in ReScript (equivalent to `getName(myStudent)`). This allows us to have the readability of OOP without the downside of dragging in a huge class system just to call a function on a piece of data. ## Tips & Tricks Do **not** abuse pipes; they're a means to an end. Inexperienced engineers sometimes shape a library's API to take advantage of the pipe. This is backwards. ## JS Method Chaining _This section requires understanding of [our binding API](bind-to-js-function.md#object-method)_. JavaScript's APIs are often attached to objects, and are often chainable, like so: Assuming we don't need the chaining behavior above, we'd bind to each case of this using [`@send`](/syntax-lookup#send-decorator) from the aforementioned binding API page: You'd use them like this: This looks much worse than the JS counterpart! Clean it up visually with pipe: ## Pipe Into Variants You can pipe into a variant's constructor as if it was a function: We turn this into: **Note** that using a variant constructor as a function wouldn't work anywhere else beside here. ## Pipe Placeholders A placeholder is written as an underscore and it tells ReScript that you want to fill in an argument of a function later. These two have equivalent meaning: Sometimes you don't want to pipe the value you have into the first position. In these cases you can mark a placeholder value to show which argument you would like to pipe into. Let's say you have a function `namePerson`, which takes a `person` then a `name` argument. If you are transforming a person then pipe will work as-is: If you have a name that you want to apply to a person object, you can use a placeholder: This allows you to pipe into any positional argument. It also works for named arguments: ## Triangle Pipe (Deprecated) You might see usages of another pipe, `|>`, in some codebases. These are deprecated. Unlike `->` pipe, the `|>` pipe puts the subject as the last (not first) argument of the function. `a |> f(b)` turns into `f(b, a)`. For a more thorough discussion on the rationale and differences between the two operators, please refer to the [Data-first and Data-last comparison by Javier Chávarri](https://www.javierchavarri.com/data-first-and-data-last-a-comparison/) # Polymorphic Variant Polymorphic variants (or poly variant) are a cousin of [variant](variant). With these differences: - They start with a `#` and the constructor name doesn't need to be capitalized. - They don't require an explicit type definition. The type is inferred from usage. - Values of different poly variant types can share the constructors they have in common (aka, poly variants are "structurally" typed, as opposed to ["nominally" typed](variant#variant-types-are-found-by-field-name)). They're a convenient and useful alternative to regular variants, but should **not** be abused. See the drawbacks at the end of this page. ## Creation We provide 3 syntaxes for a poly variant's constructor: **Take a look at the output**. Poly variants are _great_ for JavaScript interop. For example, you can use it to model JavaScript string and number enums like TypeScript, but without confusing their accidental usage with regular strings and numbers. `myColor` uses the common syntax. The second and third syntaxes are to support expressing strings and numbers more conveniently. We allow the second one because otherwise it'd be invalid syntax since symbols like `-` and others are usually reserved. ## Type Declaration Although **optional**, you can still pre-declare a poly variant type: These types can also be inlined, unlike for regular variant: **Note**: because a poly variant value's type definition is **inferred** and not searched in the scope, the following snippet won't error: That `myColor` parameter's type is inferred to be `#red`, `#green` or `#yellow`, and is unrelated to the `color` type. If you intended `myColor` to be of type `color`, annotate it as `myColor: color` in any of the places. ## Constructor Arguments This is similar to a regular variant's [constructor arguments](variant#constructor-arguments): ### Combine Types and Pattern Match You can use poly variant types within other poly variant types to create a sum of all constructors: There's also some special [pattern matching](./pattern-matching-destructuring) syntax to match on constructors defined in a specific poly variant type: This is a shorter version of: ## Structural Sharing Since poly variants value don't have a source of truth for their type, you can write such code: With a regular variant, the line `displayColor(myColor)` would fail, since it'd complain that the type of `myColor` doesn't match the type of `v`. No problem with poly variant. ## JavaScript Output Poly variants are great for JavaScript interop! You can share their values to JS code, or model incoming JS values as poly variants. - `#red` and `#"I am red 😃"` compile to JavaScipt `"red"` and `"I am red 😃"`. - `#1` compiles to JavaScript `1`. - Poly variant constructor with 1 argument, like `Instagram("Jenny")` compile to a straightforward `{NAME: "Instagram", VAL: "Jenny"}`. 2 or more arguments like `#Facebook("Josh", 26)` compile to a similar object, but with `VAL` being an array of the arguments. ### Bind to Functions For example, let's assume we want to bind to `Intl.NumberFormat` and want to make sure that our users only pass valid locales, we could define an external binding like this: The JS output is identical to handwritten JS, but we also get to enjoy type errors if we accidentally write `makeNumberFormat(#"de-DR")`. More advanced usage examples for poly variant interop can be found in [Bind to JS Function](bind-to-js-function#constrain-arguments-better). ### Bind to String Enums Let's assume we have a TypeScript module that expresses following enum export: For this particular example, we can also inline poly variant type definitions to design the type for the imported `myDirection` value: Again: since we were using poly variants, the JS Output is practically zero-cost and doesn't add any extra code! ## Extra Constraints on Types The previous poly variant type annotations we've looked at are the regular "closed" kind. However, there's a way to express "I want at least these constructors" (lower bound) and "I want at most these constructors" (upper bound): **Note:** We added this info for educational purposes. In most cases you will not want to use any of this stuff, since it makes your APIs pretty unreadable / hard to use. ### Closed `[` This is the simplest poly variant definition, and also the most practical one. Like a common variant type, this one defines an exact set of constructors. In the example above, `color` will only allow one of the three constructors that are defined in the `rgb` type. This is usually the way how poly variants should be defined. In case you want to define a type that is extensible, you'll need to use the lower / upper bound syntax. ### Lower Bound `[>` A lower bound defines the minimum set of constructors a poly variant type is aware of. It is also considered an "open poly variant type", because it doesn't restrict any additional values. Here is an example on how to make a minimum set of `basicBlueTones` extensible for a new `color` type: Here, the compiler will enforce the user to define `#Blue | #DeepBlue | #LightBlue` as the minimum set of constructors when trying to extend `basicBlueTone<'a>`. **Note:** Since we want to define an extensible poly variant, we need to provide a type placeholder `<'a>`, and also add `as 'a` after the poly variant declaration, which essentially means: "Given type `'a` is constraint to the minimum set of constructors (`#Blue | #DeepBlue | #LightBlue`) defined in `basicBlueTone`". ### Upper Bound `[<` The upper bound works in the opposite way than a lower bound: the extending type may only use constructors that are stated in the upper bound constraint. Here another example, but with red colors: ## Coercion You can convert a poly variant to a `string` or `int` at no cost: **Note**: for the coercion to work, the poly variant type needs to be closed; you'd need to annotate it, since otherwise, `theCompany` would be inferred as `[> #Apple]`. ## Tips & Tricks ### Variant vs Polymorphic Variant One might think that polymorphic variants are superior to regular [variants](./variant). As always, there are trade-offs: - Due to their "structural" nature, poly variant's type errors might be more confusing. If you accidentally write `#blur` instead of `#blue`, ReScript will still error but can't indicate the correct source as easily. Regular variants' source of truth is the type definition, so the error can't go wrong. - It's also harder to refactor poly variants. Consider this: Refactoring the first one to `#Orange` doesn't mean we should refactor the third one. Therefore, the editor plugin can't touch the second one either. Regular variant doesn't have such problem, as these 2 values presumably come from different variant type definitions. - You might lose some nice pattern match checks from the compiler: Because there's no poly variant definition, it's hard to know whether the `#blue` case can be safely removed. In most scenarios, we'd recommend to use regular variants over polymorphic variants, especially when you are writing plain ReScript code. In case you want to write zero-cost interop bindings or generate clean JS output, poly variants are oftentimes a better option. # Primitive Types ReScript comes with the familiar primitive types like `string`, `int`, `float`, etc. ## String ReScript `string`s are delimited using **double** quotes (single quotes are reserved for the character type below). To concatenate strings, use `++`: ### String Interpolation There's a special syntax for string that allows - multiline string just like before - no special character escaping - Interpolation This is just like JavaScript's backtick string interpolation, except without needing to escape special characters. ### Usage See the familiar `String` API in the [API docs](api/core/string). Since a ReScript string maps to a JavaScript string, you can mix & match the string operations in all standard libraries. ### Tips & Tricks **You have a good type system now!** In an untyped language, you'd often overload the meaning of string by using it as: - a unique id: `var BLUE_COLOR = "blue"` - an identifier into a data structure: `var BLUE = "blue" var RED = "red" var colors = [BLUE, RED]` - the name of an object field: `person["age"] = 24` - an enum: `if (audio.canPlayType() === 'probably') {...}` [(ಠ_ಠ)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType#Return_value) - other crazy patterns you'll soon find horrible, after getting used to ReScript's alternatives. The more you overload the poor string type, the less the type system (or a teammate) can help you! ReScript provides concise, fast and maintainable types & data structures alternatives to the use-cases above (e.g. variants, in a later section). ## Char ReScript has a type for a string with a single letter: **Note**: Char doesn't support Unicode or UTF-8 and is therefore not recommended. To convert a String to a Char, use `String.get("a", 0)`. To convert a Char to a String, use `String.make(1, 'a')`. ## Regular Expression ReScript regular expressions compile cleanly to their JavaScript counterpart: A regular expression like the above has the type `Re.t`. The [RegExp](api/core/regexp) module contains the regular expression helpers you have seen in JS. ## Boolean A ReScript boolean has the type `bool` and can be either `true` or `false`. Common operations: - `&&`: logical and. - `||`: logical or. - `!`: logical not. - `<=`, `>=`, `<`, `>` - `==`: structural equal, compares data structures deeply. `(1, 2) == (1, 2)` is `true`. Convenient, but use with caution. - `===`: referential equal, compares shallowly. `(1, 2) === (1, 2)` is `false`. `let myTuple = (1, 2); myTuple === myTuple` is `true`. - `!=`: structural unequal. - `!==`: referential unequal. ReScript's `true/false` compiles into a JavaScript `true/false`. ## Integers 32-bits, truncated when necessary. We provide the usual operations on them: `+`, `-`, `*`, `/`, etc. See [Int](api/core/int) for helper functions. **Be careful when you bind to JavaScript numbers!** Since ReScript integers have a much smaller range than JavaScript numbers, data might get lost when dealing with large numbers. In those cases it’s much safer to bind the numbers as **float**. Be extra mindful of this when binding to JavaScript Dates and their epoch time. To improve readability, you may place underscores in the middle of numeric literals such as `1_000_000`. Note that underscores can be placed anywhere within a number, not just every three digits. ## Floats Float requires other operators: `+.`, `-.`, `*.`, `/.`, etc. Like `0.5 +. 0.6`. See [Float](api/core/float) for helper functions. As with integers, you may use underscores within literals to improve readability. ### Int-to-Float Coercion `int` values can be coerced to `float` with the `:>` (type coercion) operator. ## Big Integers (experimental) **Since 11.1** For values which are too large to be represented by Int or Float, there is the `bigint` primitive type. We provide the usual operations on them: `+`, `-`, `*`, `/`, etc. See [BigInt](api/core/bigint) for helper functions. A `bigint` number is denoted by a trailing `n` like so: `42n`. As `bigint` is a different data type than `int`, it's necessary to open the corresponding module to overload the operators. It also supports all the bitwise operations, except unsigned shift right (`>>>`), which is not supported by JS itself for `bigint`s. It can also be pattern-matched. ## Unit The `unit` type indicates the absence of a specific value. It has only a single value, `()`, which acts as a placeholder when no other value exists or is needed. It compiles to JavaScript's `undefined` and resembles the `void` type in languages such as C++. What's the point of such a type? Consider the `Math.random` function. Its type signature is `unit => float`, which means it receives a `unit` as input and calculates a random `float` as output. You use the function like this - `let x = Math.random()`. Notice `()` as the first and only function argument. Imagine a simplified `Console.log` function that prints a message. Its type signature is `string => unit` and you'd use it like this `Console.log("Hello!")`. It takes a string as input, prints it, and then returns nothing useful. When `unit` is the output of a function it means the function performs some kind of side-effect. ## Unknown The `unknown` type represents values with contents that are a mystery or are not 100% guaranteed to be what you think they are. It provides type-safety when interacting with data received from an untrusted source. For example, suppose an external function is supposed to return a `string`. It might. But if the documentation is not accurate or the code has bugs, the function could return `null`, an `array`, or something else you weren't expecting. The ReScript type system helps you avoid run-time crashes and unpredicatable behavior by preventing you from using `unknown` in places that expect a `string` or `int` or some other type. The ReScript core libraries also provide utility functions to help you inspect `unknown` values and access their contents. In some cases you may need a JSON parsing library to convert `unknown` values to types you can safely use. Consider using `unknown` when receiving data from [external JavaScript functions](/docs/manual/latest/bind-to-js-function) # Project Structure These are the existing, non-codified community practices that are currently propagated through informal agreement. We might remove some of them at one point, and enforce some others. Right now, they're just recommendations for ease of newcomers. ## File Casing Capitalized file names (aka first letter upper-cased). **Justification**: Module names can only be capitalized. Newcomers often ask how a file maps to a module, and why `draw.res` maps to the module `Draw`, and sometimes try to refer to a module through uncapitalized identifiers. Using `Draw.res` makes this mapping more straightforward. It also helps certain file names that'd be awkward in uncapitalized form: `uRI.res`. ## Ignore `.merlin` File This is generated by the build system and you should not have to manually edit it. Don't check it into the repo. **Justification**: `.merlin` is for editor tooling. The file contains absolute paths, which are also not cross-platform (e.g. Windows paths are different). ## Folders Try not to have too many nested folders. Keep your project flat, and have fewer files (reminder: you can use nested modules). **Justification**: The file system is a _tree_, but your code's dependencies are a _graph_. Because of that, any file & folder organization is usually imperfect. While it's still valuable to group related files together in a folder, the time wasted debating & getting decision paralysis over these far outweight their benefits. We'll always recommend you to Get Work Done instead of debating about these issues. ## Third-party Dependencies Keep them to a minimum. **Justification**: A compiled, statically typed language cannot model its dependencies easily by muddling along like in a dynamic language, especially when we're still piggy-backing on NPM/Yarn (to reduce learning overhead in the medium-term). Keeping dependencies simple & lean helps reduce possibility of conflicts (e.g. two diamond dependencies, or clashing interfaces). ## Documentation Have them. Spend more effort making them great (examples, pitfalls) and professional rather than _just_ fancy-looking. Do use examples, and avoid using names such as `foo` and `bar`. There's always more concrete names (it's an example, no need to be abstract/generalized just yet. The API docs will do this plentily). For blog posts, don't repeat the docs themselves, describe the _transition_ from old to new, and why (e.g. "it was a component, now it's a function, because ..."). **Justification**: It's hard for newcomers to distinguish between a simple/decent library and one that's fancy-looking. For the sake of the community, don't try too hard to one-up each other's libraries. Do spread the words, but use your judgement too. ## PPX & Other Meta-tools Keep them to a minimum. PPX, unless used in renown cases (printer, accessors and serializer/deserializer generation), can cause big learning churn for newcomers; on top of the syntax, semantics, types, build tool & FFI that they already have to learn, learning per-library custom transformations of the code is an extra step. More invasive macros makes the code itself less semantically meaningful too, since the essence would be hiding somewhere else. ## Paradigm Don't abuse overly fancy features. Do leave some breathing room for future APIs but don't over-architect things. **Justification**: Simple code helps newcomers understand and potentially contribute to your code. Contributing is the best way for them to learn. The extra help you receive might also surpass the gain of using a slightly more clever language trick. But do try new language tricks in some of more casual projects! You might discover new ways of architecting code. ## Publishing If it's a wrapper for a JS library, don't publish the JS artifacts. If it's a legit library, publish the artifacts in lib/js if you think JS consumers might use it. This is especially the case when you gradually convert a JS lib to ReScript while not breaking existing JS consumers. Do put the keywords `"rescript"` in your package.json `keywords` field. This allows us to find the library much more easily for future purposes. **Justification**: Be nice to JS consumers of your library. They're your future ReScripters. # Promise > **Note:** Starting from ReScript 10.1 and above, we recommend using [async / await](./async-await) when interacting with Promises. ## `promise` type **Since 10.1** In ReScript, every JS promise is represented with the globally available `promise<'a>` type. For ReScript versions < 10.1, use its original alias `Js.Promise.t<'a>` instead. Here's a usage example in a function signature: To work with promise values (instead of using `async` / `await`) you may want to use the built-in `Promise` module. ## Promise A builtin module to create, chain and manipulate promises. ### Creating a promise ### Access the contents and transform a promise For comparison, the `async` / `await` version of the same code would look like this: Needless to say, the async / await version offers better ergonomics and less opportunities to run into type issues. ### Handling Rejected Promises You can handle a rejected promise using the [`Promise.catch()`](./api/core/promise#value-catch) method, which allows you to catch and manage errors effectively. ### Run multiple promises in parallel In case you want to launch multiple promises in parallel, use `Promise.all`: ## Js.Promise module (legacy - do not use) > **Note:** The `Js.Promise` bindings are following the outdated data-last convention from a few years ago. We kept those APIs for backwards compatibility. Either use [`Promise`](api/core/promise) or a third-party promise binding instead. ReScript has built-in support for [JavaScript promises](api/js/promise). The 3 functions you generally need are: - `Js.Promise.resolve: 'a => Js.Promise.t<'a>` - `Js.Promise.then_: ('a => Js.Promise.t<'b>, Js.Promise.t<'a>) => Js.Promise.t<'b>` - `Js.Promise.catch: (Js.Promise.error => Js.Promise.t<'a>, Js.Promise.t<'a>) => Js.Promise.t<'a>` Additionally, here's the type signature for creating a promise on the ReScript side: This type signature means that `make` takes a callback that takes 2 named arguments, `resolve` and `reject`. Both arguments are themselves [uncurried callbacks]( function.md#uncurried-function) (with a dot). `make` returns the created promise. ### Usage Using the [pipe operator](pipe.md): # Record Records are like JavaScript objects but: - are immutable by default - have fixed fields (not extensible) ## Type Declaration A record needs a mandatory type declaration: ## Creation To create a `person` record (declared above): When you create a new record value, ReScript tries to find a record type declaration that conforms to the shape of the value. So the `me` value here is inferred as of type `person`. The type is found by looking above the `me` value. **Note**: if the type instead resides in another file or module, you need to explicitly indicate which file or module it is: In both `me` and `me2` the record definition from `School` is found. The first one, `me` with the regular type annotation, is preferred. ## Access Use the familiar dot notation: ## Immutable Update New records can be created from old records with the `...` spread operator. The original record isn't mutated. **Note**: spread cannot add new fields to the record value, as a record's shape is fixed by its type. ## Mutable Update Record fields can optionally be mutable. This allows you to efficiently update those fields in-place with the `=` operator. Fields not marked with `mutable` in the type declaration cannot be mutated. ## JavaScript Output ReScript records compile to straightforward JavaScript objects; see the various JS output tabs above. ## Optional Record Fields ReScript [`v10`](/blog/release-10-0-0#experimental-optional-record-fields) introduced optional record fields. This means that you can define fields that can be omitted when creating the record. It looks like this: Notice how `name` has a suffixed `?`. That means that the field itself is _optional_. ### Creation You can omit any optional fields when creating a record. Not setting an optional field will default the field's value to `None`: This has consequences for pattern matching, which we'll expand a bit on soon. ## Immutable Update Updating an optional field via an immutable update above lets you set that field value without needing to care whether it's optional or not. However, if you want to set the field to an optional value, you prefix that value with `?`: You can unset an optional field's value via that same mechanism by setting it to `?None`. ### Pattern Matching on Optional Fields [Pattern matching](pattern-matching-destructuring), one of ReScript's most important features, has two caveats when you deal with optional fields. When matching on the value directly, it's an `option`. Example: But, when matching on the field as part of the general record structure, it's treated as the underlying, non-optional value: Sometimes you _do_ want to know whether the field was set or not. You can tell the pattern matching engine about that by prefixing your option match with `?`, like this: ## Record Type Spread In ReScript v11, you can now spread one or more record types into a new record type. It looks like this: `type c` will now be: Record type spreads act as a 'copy-paste' mechanism for fields from one or more records into a new record. This operation inlines the fields from the spread records directly into the new record definition, while preserving their original properties, such as whether they are optional or mandatory. It's important to note that duplicate field names are not allowed across the records being spread, even if the fields have the same type. ## Record Type Coercion Record type coercion gives us more flexibility when passing around records in our application code. In other words, we can now coerce a record `a` to be treated as a record `b` at the type level, as long as the original record `a` contains the same set of fields in `b`. Here's an example: Notice how we _coerced_ the value `a` to type `b` using the coercion operator `:>`. This works because they have the same record fields. This is purely at the type level, and does not involve any runtime operations. Additionally, we can also coerce records from `a` to `b` whenever `a` is a super-set of `b` (i.e. `a` containing all the fields of `b`, and more). The same example as above, slightly altered: Notice how `a` now has more fields than `b`, but we can still coerce `a` to `b` because `b` has a subset of the fields of `a`. In combination with [optional record fields](/docs/manual/latest/record#optional-record-fields), one may coerce a mandatory field of an `option` type to an optional field: ## Tips & Tricks ### Record Types Are Found By Field Name With records, you **cannot** say "I'd like this function to take any record type, as long as they have the field `age`". The following **won't work as intended**: Instead, `getAge` will infer that the parameter `entity` must be of type `monster`, the closest record type with the field `age`. The following code's last line fails: The type system will complain that `me` is a `person`, and that `getAge` only works on `monster`. If you need such capability, use ReScript objects, described [here](object.md). ### Optional Fields in Records Can Be Useful for Bindings Many JavaScript APIs tend to have large configuration objects that can be a bit annoying to model as records, since you previously always needed to specify all record fields when creating a record. Optional record fields, introduced in [`v10`](/blog/release-10-0-0#experimental-optional-record-fields), is intended to help with this. Optional fields will let you avoid having to specify all fields, and let you just specify the one's you care about. A significant improvement in ergonomics for bindings and other APIs with for example large configuration objects. ## Design Decisions After reading the constraints in the previous sections, and if you're coming from a dynamic language background, you might be wondering why one would bother with record in the first place instead of straight using object, since the former needs explicit typing and doesn't allow different records with the same field name to be passed to the same function, etc. 1. The truth is that most of the times in your app, your data's shape is actually fixed, and if it's not, it can potentially be better represented as a combination of variant (introduced next) + record instead. 2. Since a record type is resolved through finding that single explicit type declaration (we call this "nominal typing"), the type error messages end up better than the counterpart ("structural typing", like for tuples). This makes refactoring easier; changing a record type's fields naturally allows the compiler to know that it's still the same record, just misused in some places. Otherwise, under structural typing, it might get hard to tell whether the definition site or the usage site is wrong. # Reserved Keywords > **Note**: Some of these words are reserved purely for backward compatibility. > > If you _need_ to use one of these names as binding and/or field name, see [Use Illegal Identifier Names](use-illegal-identifier-names.md). - `and` - `as` - `assert` - `constraint` - `else` - `exception` - `external` * `false` * `for` - `if` - `in` - `include` * `lazy` * `let` - `module` - `mutable` - `of` - `open` - `rec` - `switch` - `true` - `try` - `type` - `when` - `while` - `with` # Scoped Polymorphic Types Scoped Polymorphic Types in ReScript are functions with the capability to handle arguments of any type within a specific scope. This feature is particularly valuable when working with JavaScript APIs, as it allows your functions to accommodate diverse data types while preserving ReScript's strong type checking. ## Definition and Usage Scoped polymorphic types in ReScript offer a flexible and type-safe way to handle diverse data types within specific scopes. This documentation provides an example to illustrate their usage in a JavaScript context. ### Example: Logging API Consider a logging example within a JavaScript context that processes various data types: In ReScript, we can bind to this function as a record with a scoped polymorphic function type: The `logger` type represents a record with a single field `log`, which is a scoped polymorphic function type `'a. 'a => unit`. The `'a` indicates a type variable that can be any type within the scope of the `log` function. Now, we can utilize the function obtained from `getLogger`: In this example, we create an instance of the logger by calling `getLogger()`, and then we can use the `log` function on the `myLogger` object to handle different data types. ## Limitations of Normal Polymorphic Types Let's consider the same logging example in ReScript, but this time using normal polymorphic types: In this case, the `logger` type is a simple polymorphic function type `'a => unit`. However, when we attempt to use this type in the same way as before, we encounter an issue: The problem arises because the type inference in ReScript assigns a concrete type to the `logger` function based on the first usage. In this example, after the first call to `myLogger`, the compiler infers the type `logger` for `myLogger`. Consequently, when we attempt to pass an argument of type `number` in the next line, a type error occurs because it conflicts with the inferred type `logger`. In contrast, scoped polymorphic types, such as `'a. 'a => unit`, overcome this limitation by allowing type variables within the scope of the function. They ensure that the type of the argument is preserved consistently within that scope, regardless of the specific value used in the first invocation. ## Limitations of Scoped Polymorphic Types Scoped polymorphic types work only when they are directly applied to let-bindings or record fields (as demonstrated in the logger example above). They can neither be applied to function bodies, nor to separate type definitions: # Shared Data Types ReScript's built-in values of type `string`, `float`, `array` and a few others have a rather interesting property: they compile to the exact same value in JavaScript! This means that if you're passing e.g. a ReScript string to the JavaScript side, the JS side can directly use it as a native JS string. It also means that you can import a JS string and pretend it's a native ReScript string. Unlike most compiled-to-js languages, in ReScript, **you don't need to write data converters back and forth for most of our values**! **Shared, bidirectionally usable types**: - String. ReScript strings are JavaScript strings, vice-versa. (Caveat: only our backtick string `` `hello 👋 ${personName}` `` supports unicode and interpolation). - Float. ReScript floats are JS numbers, vice-versa. - Array. In addition to the [Array API](api/core/array), we provide our own [Belt.Array](api/belt/array#set) API too. - Tuple. Compiles to a JS array. You can treat a fixed-sized, heterogenous JS array as ReScript tuple too. - Boolean. - Record. Record compiles to JS object. Therefore you can also treat JS objects as records. If they're too dynamic, consider modeling them on the ReScript side as a hashmap/dictionary [`Dict`](api/core/dict) or a ReScript object. - Object. ReScript objects are JavaScript objects, vice-versa. - Function. They compile to clean JS functions. - Module. ReScript files are considered top-level modules, and are compiled to JS files 1 to 1. Nested modules are compiled to JavaScript objects. - Polymorphic variants. - Unit. The `unit` type, which has a single value `()`, compiles to `undefined` too. Likewise, you can treat an incoming JS `undefined` as `()` if that's the only value it'll ever be. **Types that are slightly different than JS, but that you can still use from JS**: - Int. **Ints are 32-bits**! Be careful, you can potentially treat them as JS numbers and vice-versa, but if the number's large, then you better treat JS numbers as floats. For example, we bind to `Date` using `float`s. - Option. The `option` type's `None` value compiles into JS `undefined`. The `Some` value, e.g. `Some(5)`, compiles to `5`. Likewise, you can treat an incoming JS `undefined` as `None`. **JS `null` isn't handled here**. If your JS value can be `null`, use [Nullable](api/core/nullable) helpers. - Exception. - Variant. Check the compiled JavaScript output of variant to see its shape. We don't recommend exporting a ReScript variant for pure JS usage, since they're harder to read as plain JS code, but you can do it. - List, which is just a regular variant. **Non-shared types (aka internal types)**: - Character. - Int64. - Lazy values. - Everything else. Many of these are stable, which means that you can still serialize/deserialize them as-is without manual conversions. But we discourage actively peeking into their structure otherwise. These types require manual conversions if you want to export them for JS consumption. For a seamless JS/TypeScript integration experience, you might want to use [genType](https://github.com/cristianoc/gentype) instead of doing conversions by hand. # Tagged templates **Since 11.1** Tagged templates provide a special form of string interpolation, enabling the creation of template literals where placeholders aren't restricted to strings. Moreover, the resulting output isn't confined solely to strings either. You can take a look at the [JS documentation about tagged templates](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) to learn more about them. ## Define a tag function Tag functions in ReScript have the following signature: As you can see, you can have any type you want both for the placeholder array and for the output. Given how string interpolation works, you'll always have the following invariant: Let's say you want to interpolate strings with all kind of builtin types and make it work inside React components, you can define the following tag function: ## Write tagged template literals Now that you have defined your tag function, you can use it this way: Pretty neat, isn't it? As you can see, it looks like any regular template literal but it accepts placeholders that are not strings and it outputs something that is not a string either, a `React.element` in this case.## Try Online Our [Playground](/try) lets you try ReScript online, and comes with the [ReScript React bindings](/docs/react/latest/introduction) and the new [ReScript Core](https://github.com/rescript-lang/rescript-core) standard library preinstalled. # Tuple Tuples are a ReScript-specific data structure that don't exist in JavaScript. They are: - immutable - ordered - fix-sized at creation time - heterogeneous (can contain different types of values) Tuples' types can be used in type annotations as well. Tuple types visually resemble tuples values. **Note**: there's no tuple of size 1. You'd just use the value itself. ## Usage To get a specific member of a tuple, destructure it: The `_` means you're ignoring the indicated members of the tuple. Tuples aren't meant to be updated mutatively. You'd create new ones by destructuring the old ones: ## Tips & Tricks You'd use tuples in handy situations that pass around multiple values without too much ceremony. For example, to return many values: Try to keep the usage of tuple **local**. For data structures that are long-living and passed around often, prefer a **record**, which has named fields. # Type Types are the highlight of ReScript! They are: - **Strong**. A type can't change into another type. In JavaScript, your variable's type might change when the code runs (aka at runtime). E.g. a `number` variable might change into a `string` sometimes. This is an anti-feature; it makes the code much harder to understand when reading or debugging. - **Static**. ReScript types are erased after compilation and don't exist at runtime. Never worry about your types dragging down performance. You don't need type info during runtime; we report all the information (especially all the type errors) during compile time. Catch the bugs earlier! - **Sound**. This is our biggest differentiator versus many other typed languages that compile to JavaScript. Our type system is guaranteed to **never** be wrong. Most type systems make a guess at the type of a value and show you a type in your editor that's sometime incorrect. We don't do that. We believe that a type system that is sometime incorrect can end up being dangerous due to expectation mismatches. - **Fast**. Many developers underestimate how much of their project's build time goes into type checking. Our type checker is one of the fastest around. - **Inferred**. You don't have to write down the types! ReScript can deduce them from their values. Yes, it might seem magical that we can deduce all of your program's types, without incorrectness, without your manual annotation, and do so quickly. Welcome to ReScript =). The following sections explore more of our type system. ## Inference This let-binding doesn't contain any written type: ReScript knows that `score` is an `int`, judging by the value `10`. This is called **inference**. Likewise, it also knows that the `add` function takes 2 `int`s and returns an `int`, judging from the `+` operator, which works on ints. ## Type Annotation But you can also optionally write down the type, aka annotate your value: If the type annotation for `score` doesn't correspond to our inferred type for it, we'll show you an error during compilation time. We **won't** silently assume your type annotation is correct, unlike many other languages. You can also wrap any expression in parentheses and annotate it: Note: in the last line, `(~radius as r: int)` is a labeled argument. More on this in the [function](function.md) page. ## Type Alias You can refer to a type by a different name. They'll be equivalent: ## Type Parameter (Aka Generic) Types can accept parameters, akin to generics in other languages. The parameters' names **need** to start with `'`. The use-case of a parameterized type is to kill duplications. Before: After: Note that the above codes are just contrived examples for illustration purposes. Since the types are inferred, you could have just written: The type system infers that it's a `(int, int, int)`. Nothing else needed to be written down. Type arguments appear in many places. Our `array<'a>` type is such a type that requires a type parameter. If types didn't accept parameters, the standard library would need to define the types `arrayOfString`, `arrayOfInt`, `arrayOfTuplesOfInt`, etc. That'd be tedious. Types can receive many arguments, and be composable. ## Recursive Types Just like a function, a type can reference itself within itself using `rec`: ## Mutually Recursive Types Types can also be _mutually_ recursive through `and`: ## Type Escape Hatch ReScript's type system is robust and does not allow dangerous, unsafe stuff like implicit type casting, randomly guessing a value's type, etc. However, out of pragmatism, we expose a single escape hatch for you to "lie" to the type system: This declaration converts a `myType1` of your choice to `myType2` of your choice. You can use it like so: Obviously, do **not** abuse this feature. Use it tastefully when you're working with existing, overly dynamic JS code, for example. More on externals [here](external.md). **Note**: this particular `external` is the only one that isn't preceded by a `@` [attribute](attribute.md). # ReScript & TypeScript The ReScript compiler includes a code generation tool that lets you export ReScript values and types to use in TypeScript, and import TypeScript values and types into ReScript. It is called "genType". The implementation of genType performs a type-directed transformation of ReScript programs after compilation. The transformed programs operate on data types idiomatic to TypeScript. For example, a ReScript variant (which is represented as custom objects with tags at runtime): is exported to a TypeScript type: ## A Quick Example Let's assume we are working on a TypeScript codebase and we want to integrate a single ReScript function. We want to be able to import the function like any other one in our existing TypeScript code, but we also want to preserve all the ReScript types in the TypeScript type system. **That's exactly what genType was made for!** First we'll set up a function: On a successful compile, `genType` will convert `src/Color.res` to a TypeScript file called `src/Color.gen.tsx` which will look something like this: genType automatically maps the `color` variant to TS via a string union type `"Red" | "Blue"`. Within our TypeScript application, we can now import and use the function in the following manner: ## Exporting an entire module _Since ReScript `11.0.0`_ modules can be annotated with `@genType` as well. In that case, all types and values of the module will be converted to TS types. Example: ## Setup Add a `gentypeconfig` section to your `rescript.json` (See [Configuration](/docs/manual/latest/build-configuration#gentypeconfig) for details). Every `genType` powered project requires a configuration item `"gentypeconfig"` at top level in the project's `rescript.json`. The minimal configuration of genType is following: And don't forget to make sure `allowJs` is set to `true` in the project's `tsconfig.json`: ### TypeScript Module Resolutions Make sure to set the same `moduleResolution` value in both `rescript.json` and `tsconfig.json`, so that the output of genType is done with the preferred module resolution. For example if the TypeScript project uses JavaScript modules with `Node16` / `NodeNext` module resolution: Then `moduleResolution` in `gentypeconfig` should be same value: In case of the TypeScript project using `Bundler` module resolution, `allowImportingTsExtensions` should also be `true`: ## Testing the Whole Setup Open any relevant `*.res` file and add `@genType` annotations to any bindings / values / functions to be used from JavaScript. If an annotated value uses a type, the type must be annotated too. See e.g. [Hooks.res](https://github.com/rescript-lang/rescript/blob/master/tests/gentype_tests/typescript-react-example/src/Hooks.res). Save the file and rebuild the project via `npm run res:build` or similar. You should now see a `*.gen.tsx` file with the same name (e.g. `MyComponent.res` -> `MyComponent.gen.tsx`). Any values exported from `MyComponent.res` can then be imported from TypeScript. For example: ## Experimental features These features are for experimentation only. They could be changed/removed any time, and not be considered breaking changes. - Export object and record types as interfaces. To activate, add `"exportInterfaces": true` to the configuration. The types are also renamed from `name` to `Iname`. ## Shims A shim is a TS file that provides user-provided definitions for library types. Required only if one needs to export certain basic ReScript data types to JS when one cannot modify the sources to add annotations (e.g. exporting ReScript lists), and if the types are not first-classed in genType. - Example: `Array` with format: `"RescriptModule=JavaScriptModule"` Configure your shim files within `"gentypeconfig"` in your [`rescript.json`]: and add relevant `.shim.ts` files in a directory which is visible by ReScript e.g. ``` ├── rescript.json ├── src │ ├── shims │ │ ├── Js.shim.ts │ │ ├── ReactEvent.shim.ts │ │ └── RescriptPervasives.shim.ts ``` Here are some examples: More complete example shims can be found [here](https://github.com/rescript-lang/rescript/blob/master/tests/gentype_tests/typescript-react-example/src/shims/). ## Deprecated features Features related to generating runtimes were deprecated since v11 and should no longer be used. - **`@genType("alias")`** and **`@genType.as("alias")`** - **`@genType.opaque`** - **`@genType.import`** - TypeScript Shims genType does not generate anything runtime-related, and in the near future it generates definition files (`*.d.ts`) directly (See the [roadmap](https://github.com/rescript-lang/rescript/issues/6196)). If any runtime code is required for interoperability with JavaScript / TypeScript projects, it can be written by hand, or request a relevant features (e.g. `@deriving`) to the compiler. ## Limitations - **in-source = true**. Currently only supports ReScript projects with [in-source generation](/docs/manual/latest/build-configuration#package-specs) and file suffixes that end on `.js`, like `.res.js` or `.bs.js`. - **Limited namespace support**. Currently there's limited [namespace](/docs/manual/latest/build-configuration#name-namespace) support, and only `namespace:true` is possible, not e.g. `namespace:"custom"`. # Use Illegal Identifier Names Sometime, for e.g. a let binding or a record field, you might want to use: - A capitalized name. - A name that contains illegal characters (e.g. emojis, hyphen, space). - A name that's one of ReScript's reserved keywords. We provide an escape hatch syntax for these cases: See the output. **Use them only when necessary**, for interop with JavaScript. This is a last-resort feature. If you abuse this, many of the compiler guarantees will go away. # Variant So far, most of ReScript's data structures might look familiar to you. This section introduces an extremely important, and perhaps unfamiliar, data structure: variant. Most data structures in most languages are about "this **and** that". A variant allows us to express "this **or** that". `myResponse` is a variant type with the cases `Yes`, `No` and `PrettyMuch`, which are called "variant constructors" (or "variant tag"). The `|` bar separates each constructor. **Note**: a variant's constructors need to be capitalized. ## Variant Needs an Explicit Definition If the variant you're using is in a different file, bring it into scope like you'd do [for a record](record.md#record-needs-an-explicit-definition): ## Constructor Arguments A variant's constructors can hold extra data separated by comma. Here, `Instagram` holds a `string`, and `Facebook` holds a `string` and an `int`. Usage: ### Labeled Variant Payloads (Inline Record) If a variant payload has multiple fields, you can use a record-like syntax to label them for better readability: This is technically called an "inline record", and only allowed within a variant constructor. You cannot inline a record type declaration anywhere else in ReScript. Of course, you can just put a regular record type in a variant too: The output is slightly uglier and less performant than the former. ## Variant Type Spreads Just like [with records](record#record-type-spread), it's possible to use type spreads to create new variants from other variants: Type `b` is now: Type spreads act as a 'copy-paste', meaning all constructors are copied as-is from `a` to `b`. Here are the rules for spreads to work: - You can't overwrite constructors, so the same constructor name can exist in only one place as you spread. This is true even if the constructors are identical. - All variants and constructors must share the same runtime configuration - `@unboxed`, `@tag`, `@as` and so on. - You can't spread types in recursive definitions. Note that you need a leading `|` if you want to use a spread in the first position of a variant definition. ### Pattern Matching On Variant See the [Pattern Matching/Destructuring](pattern-matching-destructuring) section later. ## JavaScript Output A variant value compiles to 3 possible JavaScript outputs depending on its type declaration: - If the variant value is a constructor with no payload, it compiles to a string of the constructor name. Example: `Yes` compiles to `"Yes"`. - If it's a constructor with a payload, it compiles to an object with the field `TAG` and the field `_0` for the first payload, `_1` for the second payload, etc. The value of `TAG` is the constructor name as string by default, but note that the name of the `TAG` field as well as the string value used for each constructor name [can be customized](#tagged-variants). - Labeled variant payloads (the inline record trick earlier) compile to an object with the label names instead of `_0`, `_1`, etc. The object will have the `TAG` field as per the previous rule. Check the output in these examples: ## Tagged variants - The `@tag` attribute lets you customize the discriminator (default: `TAG`). - `@as` attributes control what each variant case is discriminated on (default: the variant case name as string). ### Example: Binding to TypeScript enums You can bind to the above enums like so: Now, this maps 100% to the TypeScript code, including letting us bring over the documentation strings so we get a nice editor experience. ### String literals The same logic is easily applied to string literals from TypeScript, only here the benefit is even larger, because string literals have the same limitations in TypeScript that polymorphic variants have in ReScript: There's no way to attach documentation strings to string literals in TypeScript, and you only get the actual value to interact with. ### Valid `@as` payloads Here's a list of everything you can put in the `@as` tag of a variant constructor: - A string literal: `@as("success")` - An int: `@as(5)` - A float: `@as(1.5)` - True/false: `@as(true)` and `@as(false)` - Null: `@as(null)` - Undefined: `@as(undefined)` ## Untagged variants With _untagged variants_ it is possible to mix types together that normally can't be mixed in the ReScript type system, as long as there's a way to discriminate them at runtime. For example, with untagged variants you can represent a heterogenous array: Here, each value will be _unboxed_ at runtime. That means that the variant payload will be all that's left, the variant case name wrapping the payload itself will be stripped out and the payload will be all that remains. It, therefore, compiles to this JS: In the above example, reaching back into the values is as simple as pattern matching on them. ### Advanced: Unboxing rules #### No overlap in constructors A variant can be unboxed if no constructors have overlap in their runtime representation. For example, you can't have `String1(string) | String2(string)` in the same unboxed variant, because there's no way for ReScript to know at runtime which of `String1` or `String2` that `string` belongs to, as it could belong to both. The same goes for two records - even if they have fully different shapes, they're still JavaScript `object` at runtime. Don't worry - the compiler will guide you and ensure there's no overlap. #### What you can unbox Here's a list of all possible things you can unbox: - `string`: `String(string)` - `float`: `Float(float)`. Note you can only have one of `float` or `int` because JavaScript only has `number` (not actually `int` and `float` like in ReScript) so we can't disambiguate between `float` and `int` at runtime. - `int`: `Int(int)`. See note above on `float`. - `bigint`: `BigInt(int)`. **Since 11.1** This is a distinct type from JavaScript's `number` type so you can use it beside either `float` or `int`. - `bool`: `Boolean(bool)` - `array<'value>`: `List(array)` - `('a, 'b, 'c)`: `Tuple((string, int, bool))`. Any size of tuples works, but you can have only one case of array or tuple in a variant. - `promise<'value>`: `Promise(promise)` - `Dict.t`: `Object(Dict.t)` - `Date.t`: `Date(Date.t)`. A JavaScript date. - `Blob.t`: `Blob(Blob.t)`. A JavaScript blob. - `File.t`: `File(File.t)`. A JavaScript file. - `RegExp.t`: `RegExp(RegExp.t)`. A JavaScript regexp instance. Again notice that the constructor names can be anything, what matters is what's in the payload. > **Under the hood**: Untagged variants uses a combination of JavaScript `typeof` and `instanceof` checks to discern between unboxed constructors at runtime. This means that we could add more things to the list above detailing what can be unboxed, if there are useful enough use cases. ### Pattern matching on unboxed variants Pattern matching works the same on unboxed variants as it does on regular variants. In fact, in the perspective of ReScript's type system there's no difference between untagged and tagged variants. You can do virtually the same things with both. That's the beauty of untagged variants - they're just variants to you as a developer. Here's an example of pattern matching on an unboxed nullable value that illustrates the above: No difference to how you'd do with a regular variant. But, the runtime representation is different to a regular variant. > Notice how `@as` allows us to say that an untagged variant case should map to a specific underlying _primitive_. `Present` has a type variable, so it can hold any type. And since it's an unboxed type, only the payloads `'a` or `null` will be kept at runtime. That's where the magic comes from. ### Decoding and encoding JSON idiomatically With untagged variants, we have everything we need to define a native JSON type: Here's an example of how you could write your own JSON decoders easily using the above, leveraging pattern matching: Encoding that same structure back into JSON is also easy: This can be extrapolated to many more cases. ### Advanced: Catch-all Constructors With untagged variants comes a rather interesting capability - catch-all cases are now possible to encode directly into a variant. Let's look at how it works. Imagine you're using a third party API that returns a list of available animals. You could of course model it as a regular `string`, but given that variants can be used as "typed strings", using a variant would give you much more benefit: This is all fine and good as long as the API returns `"Dog"`, `"Cat"` or `"Bird"` for `animal`. However, what if the API changes before you have a chance to deploy new code, and can now return `"Turtle"` as well? Your code would break down because the variant `animal` doesn't cover `"Turtle"`. So, we'll need to go back to `string`, loosing all of the goodies of using a variant, and then do manual conversion into the `animal` variant from `string`, right? Well, this used to be the case before, but not anymore! We can leverage untagged variants to bake in handling of unknown values into the variant itself. Let's update our type definition first: Notice we've added `@unboxed` and the constructor `UnknownAnimal(string)`. Remember how untagged variants work? You remove the constructors and just leave the payloads. This means that the variant above at runtime translates to this (made up) JavaScript type: ``` type animal = "Dog" | "Cat" | "Bird" | string ``` So, any string not mapping directly to one of the payloadless constructors will now map to the general `string` case. As soon as we've added this, the compiler complains that we now need to handle this additional case in our pattern match as well. Let's fix that: There! Now the external API can change as much as it wants, we'll be forced to write all code that interfaces with `animal` in a safe way that handles all possible cases. All of this baked into the variant definition itself, so no need for labor intensive manual conversion. This is useful in any scenario when you use something enum-style that's external and might change. Additionally, it's also useful when something external has a large number of possible values that are known, but where you only care about a subset of them. With a catch-all case you don't need to bind to all of them just because they can happen, you can safely just bind to the ones you care about and let the catch-all case handle the rest. ## Coercion In certain situations, variants can be coerced to other variants, or to and from primitives. Coercion is always zero cost. ### Coercing Variants to Other Variants You can coerce a variant to another variant if they're identical in runtime representation, and additionally if the variant you're coercing can be represented as the variant you're coercing to. Here's an example using [variant type spreads](#variant-type-spreads): ### Coercing Variants to Primitives Variants that are guaranteed to always be represented by a single primitive at runtime can be coerced to that primitive. It works with strings, the default runtime representation of payloadless constructors: If you were to configure all of your construtors to be represented as `int` or `float`, you could coerce to those too: ### Advanced: Coercing `string` to Variant In certain situtations it's possible to coerce a `string` to a variant. This is an advanced technique that you're unlikely to need much, but when you do it's really useful. You can coerce a `string` to a variant when: - Your variant is `@unboxed` - Your variant has a "catch-all" `string` case Let's look at an example: This works because the variant is unboxed **and** has a catch-all case. So, if you throw a string at this variant that's not representable by the payloadless constructors, like `"One"` or `"Two"`, it'll _always_ end up in `Other(string)`, since that case can represent any `string`. ## Tips & Tricks **Be careful** not to confuse a constructor carrying 2 arguments with a constructor carrying a single tuple argument: ### Variants Must Have Constructors If you come from an untyped language, you might be tempted to try `type myType = int | string`. This isn't possible in ReScript; you'd have to give each branch a constructor: `type myType = Int(int) | String(string)`. The former looks nice, but causes lots of trouble down the line. ### Interop with JavaScript _This section assumes knowledge about our JavaScript interop. Skip this if you haven't felt the itch to use variants for wrapping JS functions yet_. Quite a few JS libraries use functions that can accept many types of arguments. In these cases, it's very tempting to model them as variants. For example, suppose there's a `myLibrary.draw` JS function that takes in either a `number` or a `string`. You might be tempted to bind it like so: **Try not to do that**, as this generates extra noisy output. Instead, use the `@unboxed` attribute to guide ReScript to generate more efficient code: Alternatively, define two `external`s that both compile to the same JS call: ReScript also provides [a few other ways](bind-to-js-function.md#modeling-polymorphic-function) to do this. ### Variant Types Are Found By Field Name Please refer to this [record section](record#tips--tricks). Variants are the same: a function can't accept an arbitrary constructor shared by two different variants. Again, such feature exists; it's called a polymorphic variant. We'll talk about this in the future =). ## Design Decisions Variants, in their many forms (polymorphic variant, open variant, GADT, etc.), are likely _the_ feature of a type system such as ReScript's. The aforementioned `option` variant, for example, obliterates the need for nullable types, a major source of bugs in other languages. Philosophically speaking, a problem is composed of many possible branches/conditions. Mishandling these conditions is the majority of what we call bugs. **A type system doesn't magically eliminate bugs; it points out the unhandled conditions and asks you to cover them**\*. The ability to model "this or that" correctly is crucial. For example, some folks wonder how the type system can safely eliminate badly formatted JSON data from propagating into their program. They don't, not by themselves! But if the parser returns the `option` type `None | Some(actualData)`, then you'd have to handle the `None` case explicitly in later call sites. That's all there is. Performance-wise, a variant can potentially tremendously speed up your program's logic. Here's a piece of JavaScript: There's a linear amount of branch checking here (`O(n)`). Compare this to using a ReScript variant: The compiler sees the variant, then 1. conceptually turns them into `type animal = "Dog" | "Cat" | "Bird"` 2. compiles `switch` to a constant-time jump table (`O(1)`). import { make as WarningTable } from "src/components/WarningTable.mjs"; # Warning Numbers You can configure which warnings the ReScript compiler generates [in the build configuration](/docs/manual/latest/build-configuration#warnings) or using the [`@warning()`](/syntax-lookup#expression-warning-decorator) or the [`@@warning()`](/syntax-lookup#module-warning-decorator) decorator.