Learn ReScript — A Safer Way to Write JavaScript— Part 1

Jayson Alzate
The Startup
Published in
10 min readDec 18, 2020

--

Begin writing this Facebook-backed functional programming language

Photo by Irvan Smith on Unsplash

Note: Part 2 of this tutorial is now available. We go over more advanced concepts like pattern matching, and utilizing third-party libraries. Click here to read it now!

Why learn ReScript?

ReScript is one of the most exciting new projects in my opinion. Announced in July 2020, this rebrand of the BuckleScript compiler promises to provide features tailored toward the JavaScript ecosystem. Like its Facebook-backed predecessor ReasonML, ReScript leverages the strong type system of OCaml to ensure that you get a robust type system with type inference, coupled with great built-in support for JSX, and blazing fast builds that compile to optimized JavaScript and it is a great language for React development. The best feature of ReScript is that it is built in a way that makes incremental adoption easy and painless. With such low risk and investment needed, why not try it?

Before we learn more about ReScript, it might be helpful to learn a bit about its predecessor, ReasonML.

What is ReasonML?

First released in May 2016, ReasonML was built at Facebook by React creator Jordan Walke. He had long been intrigued by OCaml, actually having originally built React in the language. He developed ReasonML as a new syntax for OCaml, that would make it more approachable for JS developers. The project has been supported by Facebook from the beginning. They even converted half of their Messenger app over to ReasonML!

A separate project originating out of Bloomberg, BuckleScript, compiles this Reason syntax back into JavaScript.

This allowed JS developers to utilize a host of benefits in their code all while maintaining strong operability with their existing code.

A few of the main benefits of ReasonML include:

  • Allows you to incrementally introduce the language into an existing app.
  • Strong type safety and top-notch inference
  • Compiles much faster than TypeScript
  • Optimized JavaScript output
  • Able to leverage OCaml and the JavaScript ecosystem
  • Pattern matching for comprehensive case handling
  • Built-in functional programming enablers
  • Compiles to native code

This video on the benefits of ReasonML does a great job of highlighting these benefits with examples.

Most of ReasonML’s awesome features made it to ReScript

Lastly, a project known as ReasonReact was developed by Facebook. This added Reason bindings for React. It even comes with built-in routing and state management solutions built into the library.

So what is ReScript?

Credit to Bettina Steinbrecher. Follow her on Twitter here

ReScript started as a rewrite of the BuckleScript syntax. This was to make the project even easier for JavaScript developers to adopt and allow ReScript to be better tailored to the needs of JavaScript users rather than being tied to the Reason project.

The only major difference between ReasonML and ReScript is a friendlier syntax for JS developers and it no longer compiles to Native.

This is intended to be a fork of the ReasonML project, but it will have backward compatibility support with the existing Reason code for the foreseeable future. I am excited to see what features the ReScript team comes up with.

Why a Tutorial?

The biggest impediment to getting started right now is that docs are currently a work in progress. Additionally, the ecosystem documentation is fragmented considering the new syntax.

The best feature of ReScript is that it is built in a way that makes incremental adoption easy and painless. With such low risk and investment needed, why not try it?

As a result, it can be tough to find a straightforward solution to common problems. I previously wrote an article on converting a basic React app to TypeScript, and I wanted to see if I could convert the app to ReScript. I was able to finally get it working with a bit of hard work and a ton of scouring the internet. As a result, I wanted to create a follow-along tutorial to help people get started with ReScript today — and save them some headaches.

As a result of being a novice user of ReScript, we will not leverage a ton of advanced features, but this tutorial should hopefully prove helpful in getting started. In short, this a tutorial for ReScript beginners written by a ReScript beginner.

This tutorial covers a lot of ground, so it will be split into 2 parts.

In part 1, we will set up our environment and go over some quick concepts. By the end of this tutorial, you should feel confident making simple React components and util files.

In part 2, we will build on the knowledge to actually convert an app completely to ReScript.

To get started, let’s first set up our environment.

Environment Setup

Before we get started let’s get you set up with the starter project and your environment.

Editor Setup

We recommend that you use Microsoft’s VSCode Editor.

We also recommend that you download the official ReScript editor plugin — rescript-vscode

Repo

You can clone the starter repo that will be used in this training from the link below.

Starter Project*

There is a branch with our final code for reference.

*Note: This starter repo is based on a modified version of Marcos Lombog’s Babel, Webpack, React starter repo. He wrote a great article on setting it up here.

Adding ReScript to Our Application

Adding ReScript to an existing application is very straightforward since we don’t need to integrate with a bundler. As a result, adding ReScript will involve 3 steps:

1 . Installing our dependencies

2 . Configuring our BuckleScript build compiler

3. Defining npm scripts to build our project

Installing BuckleScript Dependencies

We need to install a few packages that we will use in our project. They fall into two categories, ReScript ecosystem tooling, and ReScript bindings.

ReScript Ecosystem:

  • bsb-platform — Also the compiler for ReasonML, this package contains the compiler that turns ReScript to optimized JavaScript. The ReScript syntax was added in version 8.1, so make sure to install 8.1 or later.
  • gentype — Gentype allows you to generate your ReasonML or Rescript code with TypeScript, Flow, or plain JS type declaration. We will use TypeScript in our example. To learn more, here is an interesting article on its value.
  • reason-react — ReasonReact allows you to use React in ReasonML and ReScript. It includes features like routing and data management built-in.

ReScript Bindings for popular JavaScript libraries. Used in Part 2:

  • @mscharley/bs-material-ui-icons — Reason Bindings for Material UI Icons
  • @jsiebern/bs-material-ui — Reason Bindings for Material UI library. You can see the docs here.

We can install the packages with the below script.

Configure BuckleScript Compiler

To utilize ReScript, we need to have a bsconfig.json file to configure the compiler. Below is our initial configuration, but there are a number of options in the docs. I explain the most relevant options further down.

Initial bsconfig for our project
  • name — The name of our app
  • react-jsx — Value of 3 is used to enable JSX for ReasonReact.
  • refmt — Value of 3 is used to enable JSX for ReasonReact.
  • sources — Where the compiler should look for ReScript files. We also are telling the compiler to look into the sub-directories as well.
  • package-specs — Whether we export our output as CommonJS or ES6 modules. We also set inline to “true” to generate the JavaScript code next to the ReScript files.
  • suffix — Extension is set to “bs.js”. This is the extension of our outputted JavaScript files. That said some users set the file extension to just “.js”. The unique extension helps the compiler remove out-of-date files.
  • namespace — “True” value is used to avoid module naming collision when importing external packages
  • bs-dependencies — The value is “reason-react”. This field is similar to the dependencies listed in a package.json. Since we are using ReasonReact, we need to import it here.
  • gentypeconfig — This is used to tell Gentype what to export types and values as. We can select plain JS, Flow, or TypeScript. We will use TypeScript in this tutorial

Add Build Scripts for our Compiler

We will now define a build script for our compiler. We have a standalone build and a watch option that will be used for active development.

A Note about Developing in ReScript

When developing in ReScript, we will not be using Webpack to bundle our ReScript files, due to the blazing fast compiler speed. Instead, we will compile and then import the outputted JS files to our project. We would then let our bundler transform, and bundle our app. This pattern lets us slowly introduce ReScript to an app.

For the purposes of this tutorial, we just need to run the ReScript compiler and not start our app.

Run the ReScript compiler on watch mode for quick development

Basic ReScript Concepts

Here are some quick ReScript concepts to help us on our way. Now that we have added the compiler to our project you can follow along with some of the code examples below. As always, the docs are a great place to start to get more information on questions you might have.

What is ReScript again?

ReScript is a JS-like syntax that leverages parts of the OCaml language. This allows ReScript to leverage its excellent type checking system and other features of the language. The compiler then handles converting this code to optimized Javascript.

How do I integrate Rescript into my build system (Webpack, etc.)?

Since it has such a lightning-fast compile time, it is not recommended that you use Rescript with a bundling tool like Webpack. It is better to directly import the generated Javascript files into an existing project and let your bundler load those JS files.

Can I import JavaScript into ReScript?

Yes, including any libraries! You can even write JavaScript directly in your ReScript file (though it is discouraged).

What are the ReScript file extensions?

Rescript uses two potential file extensions .res for implementation or .resi for interface. In this tutorial, we will only use .res files.

What is the module system?

Each ReScript file implicitly creates a module of the same name. All contents of the file are then accessible to every other ReScript module. This means no more import statements!

A quick example: We define a variable name myVariable in Test.res . In any other file, we can access this variable like thisTest.myVariable.

What is the naming convention of ReScript Files?

Due to the module system, the convention is to use uppercase filenames.

What keyword is used to declare variables?

In Rescript, let is the only keyword used for variable declaration. There is more information in the docs.

Are there any standard libraries in ReScript?

Yes. Rescript comes with three libraries: Belt, JS, and Dom.

  • The Dom library is mostly used for libraries. Disregard for now.
  • The Belt library is the ReScript standard library. It is recommended to use the Belt library wherever possible as it provides immutable data structures as well as curried and uncurried versions of functions. It is under active development and still does not have Int or String functions yet.
  • The JS library exposes the JavaScript API even if it is unsafe. You would use this for Int and String functions as well as things like console logging.

How do I console log?

There is a method in the JS library called log. For basic logging, you can just pass what you want to log to the JS.log function. There are other methods in the Console module.

How does the type system work?

Every declaration in ReScript has a type. Except for records, we generally do not have to annotate types. The compiler infers a lot of it through the syntax of the language. Also, it should be noted that we are not allowed to perform operations on values of different types (like multiply an integer and a float). You must convert a value to do so. Please refer to the docs for more information. Below is an example.

An example of how operation syntax differ based on types

Here is the optimized output:

Here is the output. Notice that it evaluates some of the functions upon compilation.

Now that we know types, how can you generate type declarations?

We can you the Gentype tool to automatically generate type annotations for our files. You can generate types for TypeScript, Flow, and Plain JS. You just need to use the @gentype decorator. Here is the type example above with Gentype added.

You just need to put @gentype above the variable you want to generate types for

It generates the following type declaration file in TypeScript, using the typescript option:

Example of TypeScript generated types

What do I need to know about ReasonReact?

ReasonReact differs a bit from regular React. For developing, we should keep the following rules in mind to help ease the process:

1 . Every ReasonReact Component should be a standalone module. This includes any you import from Javascript

2 . Every ReasonReact Component is exported as a named export of make vs a default export in Javascript.

3. Conversely every Rescript file that defines a ReasonReact component has a variable make where you define the Component.

4. All props are passed using the named args syntax ~firstName. We can use this in our code by referring to the variable as firstName.

4. Lastly, all ReasonReact elements rendered have a method to render a specific type. This ensures type safety. Below is an example that showcases this.

A Basic React component in Reason

Which compiles to:

Our compiled Component exported as “make”

Where can I find the answer to X?

Unfortunately, while the ReScript docs are excellent, the documentation for other parts of the ecosystem is currently lacking and fragmented. Being a new-ish ecosystem, clear examples specific to ReScript can be few and far between. One tip is to look at existing ReasonML documentation. Also, we can look at the raw Rescript or Reason files within a library to get a good sense of how to work with a library. We will go through some examples in this tutorial. Lastly, I would ask your question on the forum or the Discord channel.

Conclusion

Photo by Braden Collum on Unsplash

Now that we have setup ReScript in our app and learned a bit more about some of the intricacies of the language and ecosystem, we are ready to convert an existing app over to ReScript in part 2. Throughout the process, we will work through more complex operations.

Here is a link to our finalized code.

Note: Part 2 of this tutorial is now available. We go over more advanced concepts like pattern matching, and utilizing third-party libraries. Click here to read it now!

--

--

Jayson Alzate
The Startup

Full-stack engineer working primarily in Javascript. Bootcamp grad with data analytics background and an MBA. Trying to get better every day!