ReasonML: getting started
Tags: reason, tutorial
Published: 2018-04-04 18:07:22

---
title: ReasonML: getting started
published: true
description: A tutorial on building your first app with ReasonML
tags: reason,reasonml,tutorial
---

In this tutorial, we're going to build a small weather app using Reason. There's a link to the source code at the [bottom of the page](#reason-app-demo). This tutorial assumes a basic understanding of React, as we'll be using the ReasonReact bindings to build the app. If you haven't used React before, [this article](https://dev.to/tylermcginnis/a-comprehensive-guide-to-reactjs-in-2018--4nbc) is a great place to start.

![ReasonML](https://thepracticaldev.s3.amazonaws.com/i/rndtcngsysf696cvb0wf.png)


## What is Reason?

Reason is a new syntax for OCaml, developed by Facebook with a heavy influence from JavaScript. It’s got 100% type coverage, which has resulted in a very powerful type system.

Reason is also suitable for cross-platform development. We can use BuckleScript to compile our code into (readable) JavaScript, which opens up the entire web platform. Thanks to OCaml, it's also possible to use Reason for native development.

In addition, Reason can access the entire JS and OCaml ecosystems, and offers ReasonReact for building UI components with ReactJS. There’s a [useful page in the docs](https://reasonml.github.io/docs/en/what-and-why.html) which explains the advantages in more detail!


---


## Requirements

First of all, let's make sure we've got the right tools installed.

We'll be using Create React App to bootstrap the project. If you haven't used it before, install by running `npm i -g create-react-app`. There are two other packages we need to get started:

* Reason CLI: the Reason toolchain. [Check the installation docs](https://github.com/reasonml/reason-cli#1-choose-your-platform).
  At the time of writing, macOS users can install by running `npm i -g reason-cli@3.1.0-darwin`

* BuckleScript: `npm i -g bs-platform`

I'm also using the [vscode-reasonml](https://github.com/reasonml-editor/vscode-reasonml) editor plugin. If you're using a different editor, [check the list of plugins](https://reasonml.github.io/docs/en/editor-plugins.html) to find the right one for you.


---


## Our first component

To get started, we'll create the boilerplate code for our app:

  `create-react-app weather-app --scripts-version reason-scripts`

This gives us a basic App component:

{% gist https://gist.github.com/jack-lewin/e36a4a33e304b72e342b02319ea0e669 %}

We can compile & run this using `yarn start`. Let's take a look at a few interesting parts...

> `[%bs.raw {|require('./app.css')|}];`

  BuckleScript allows us to [mix raw JavaScript](https://reasonml.github.io/docs/en/interop.html) into our Reason code, from a one-liner to an entire library (if we're just hacking around). This should be used sparingly, but can be a useful escape hatch whilst we're getting started.


> `let component = ReasonReact.statelessComponent("App");`

  We'll be using two types of ReasonReact component: `statelessComponent` and `reducerComponent`. Stateless components do what they say on the tin. Reducer components are stateful, and have Redux-like *reducers* built-in. We'll come onto these later.

> `let make = (~message, _children) => { ... }`

  This is the method that defines our component. The two parameters have different symbols: `~` is a labelled argument, meaning we can reference the parameter by name, and `_` is a more explicit way of showing that the parameter isn't used (the compiler will give us a warning otherwise).

  The `...component` spread operator means that our `make` function is building upon the component we just defined, overwriting the defaults.

> `<h2> (ReasonReact.stringToElement(message)) </h2>`

  JSX in Reason is more strict than in normal React. Instead of just writing `<h2>{message}</h2>`, we have to explicitly convert the `message` string to a JSX element.

We'll be using this boilerplate when we build our own components later on.


---


## Types in Reason

Let's create a new file, `WeatherData.re`. This will define the data structure and any related methods for our Weather record. To begin with, let's create the type:

{% gist https://gist.github.com/jack-lewin/1d61c97277987a20372a5277687d84e0 %}

Within this file, we can create new records using this data structure, and the compiler will know that it's a Weather item. From other files, we'll need to tell the compiler what the type is. In Reason, [files can be referenced](https://reasonml.github.io/docs/en/faq.html#i-don-t-see-any-import-or-require-in-my-file-how-does-module-resolution-work) as modules, meaning we don't have to explicitly import them! We can just do this:

{% gist https://gist.github.com/jack-lewin/8ffa68f546fc63727df6190ac38543df %}

I mentioned earlier that Reason has 100% type coverage, but we've only defined our Weather type... where does the rest of the coverage come from? We could explicitly define a type for every variable we use, e.g. `let greeting: string = "Hello";` but fortunately the OCaml system can *infer* types for us. So if we write `let greeting = "Hello";` the compiler will still know that `greeting` is a string. This is a key concept in Reason and guarantees type safety.


---


## Keeping state

Moving back to our project, let's modify `app.re` so it can store the data we want to display. This will involve:

1. Defining the type of our state
2. Setting our initial state (with some dummy data, for now)
3. Defining **actions** that can be applied to state
4. Defining **reducers** for the component to handle these

Actions define the different things we can do to manipulate state. For example, `Add` or `Subtract`. Reducers are pure functions which define how state should be affected by these actions, just like in Redux. They take the action and our previous state as parameters, and return an [update type](https://reasonml.github.io/reason-react/docs/en/state-actions-reducer.html#state-update-through-reducer).

{% gist https://gist.github.com/jack-lewin/da9283052245d9d257972e06e13dadb5 %}

There are two new Reason concepts here: **variants** and **pattern matching**.

> `type action =`
> ` `` ``| WeatherLoaded(WeatherData.weather);`

This is a variant: a data structure which represents a choice of different values (like enums). Each case in a variant must be capitalised, and can optionally receive parameters. In ReasonReact, actions are represented as variants. These can be used with the `switch` expression:

> `switch action {`
> `| WeatherLoaded(newWeather) =>`
> ` `` ``ReasonReact.Update({ ... })`
> `}`

This is one of the most useful features in Reason. Here we're pattern matching `action`, based on the parameter we receive in the `reducer()` method. The compiler knows that our switch statement needs to handle every case of `action`. If we forget to handle a case, the compiler knows, and will tell us!

<figure>
  ![Pattern matching error](https://thepracticaldev.s3.amazonaws.com/i/sfutmltmhu1fmzzjs9zs.png)
  <figcaption>The Reason compiler catching unhandled cases.</figcaption>
</figure> 

We used destructuring to access the value of `newWeather` in a previous example. We can also use this to match actions *based on* the values they contain. This gives us some [very powerful behaviour](https://reasonml.github.io/docs/en/pattern-matching.html)!


---


## Fetching data

So far, our app renders the dummy weather data - now let's load it from an API. We'll put the methods for fetching and parsing data in our existing `WeatherData.re` file.

Firstly, we need to install [bs-fetch](https://github.com/reasonml-community/bs-fetch): `npm i bs-fetch` and [bs-json](https://github.com/glennsl/bs-json): `npm i @glennsl/bs-json`. We also need to add them to our `bsconfig.json`:

> `{`
> ` `` ``...`
> ` `` ``"bs-dependencies": [`
> ` `` `` `` ``"bs-fetch"`
> ` `` `` `` ``"@glennsl/bs-json"`
> ` `` ``]`
> `}`

We'll be using the [Yahoo Weather API](https://developer.yahoo.com/weather) to fetch our data. Our `getWeather()` method will call the API, then parse the result using `parseWeatherResultsJson()`, before resolving with a `weather` item:

{% gist https://gist.github.com/jack-lewin/55df26f8da7daf041cd6380bdaa1f266 %}

> `Json.parseOrRaise(json) |> Json.Decode.(at([`
> ` `` ``...`
> `], parseWeatherJson));`

This parses the JSON string response, before traversing the data via the specified fields. It then uses the `parseWeatherJson()` method to parse the data found inside the `condition` field.

> `Json.Decode.{`
> ` `` ``summary: field("text", string, json),`
> ` `` ``temp: float_of_string(field("temp", string, json))`
> `};`

In this snippet, `field` and `string` are properties of `Json.Decode`. This new syntax "opens" `Json.Decode`, so its properties can be used freely within the curly brackets (instead of repeating `Json.Decode.foo`). The code generates a `weather` item, using the `text` and `temp` fields to assign `summary` and `temp` values.

`float_of_string` does exactly what you'd expect: it converts the temperature from a string (as we get from the API) into a float.


---


## Updating state

Now we've got a `getWeather()` method which returns a promise, we need to call this when our App component loads. ReasonReact has a similar set of lifecycle methods to React.js, with a [few small differences](https://reasonml.github.io/reason-react/docs/en/lifecycles.html). We'll be using the `didMount` lifecycle method for making the API call to fetch the weather.

First of all, we need to change our state to show that it's possible to *not have* a weather item in state - we'll get rid of the dummy data. `option()` is a built-in variant in Reason, which describes a "nullable" value:

  `type option('a) = None | Some('a);`

We need to specify `None` in our state type and initial state, and `Some(weather)` in our `WeatherLoaded` reducer:

{% gist https://gist.github.com/jack-lewin/b61651e82e7a294ab67a402c8e35897a %}

Now we can actually make the API request when our component mounts. Looking at the code below, `handleWeatherLoaded` is a method which dispatches our `WeatherLoaded` action to the reducer. When the promise resolves, it will be handled by our reducer, and the state will be updated!

Note: it's important to return `ReasonReact.NoUpdate` from most component lifecycles. The reducer will handle all state changes at the next opportunity.

{% gist https://gist.github.com/jack-lewin/06b18a8c6fd31c7c000fcc725cca4f96 %}

If we run our app now, we'll run into an error... We're currently trying to render information about `self.state.weather`, but this is set to `None` until we receive a response from the API. Let's update our App component to show a loading message while we wait:

{% gist https://gist.github.com/jack-lewin/badd7710d8cc850eb25c79c7891ccdd5 %}

And the result...

![ReasonReact loading message](https://im3.ezgif.com/tmp/ezgif-3-cf07dc176b.gif)


---


## Error handling

One thing we haven't thought about is what happens if we can't load our data. What if the API is down, or it returns something we're not expecting? We'll need to recognise this and reject the promise:

{% gist https://gist.github.com/jack-lewin/cb270a0e4a98937aaf756fc1544b0408 %}

> `switch (parseWeatherResultsJson(jsonText)) {`
> `| exception e => reject(e);`
> `| weather => resolve(weather);`
> `};`

This switch statement tries to parse the API response. If an exception is raised, it will reject the promise with that error. If the parsing was successful, the promise will be resolved with the weather item.

Next, we'll change our state to let us recognise if an error has occurred. Let's create a new type which adds an `Error` case to our previous `Some('a)` or `None`.

{% gist https://gist.github.com/jack-lewin/fd4de7b352f4bd79588a3b9d54cbf5e2 %}

Whilst doing this, we'll also need to add an `Error` case to our render function - I'll let you add that yourself. Finally, we need to create a new action and reducer to be used when our `getWeather()` promise rejects.

{% gist https://gist.github.com/jack-lewin/5c442971c3338cb794170f2b81b1458d %}

These are concepts we've used already, but it's useful to let the user know if something goes wrong. We don't want to leave them hanging with a "loading" message!

---


There we have it, our first ReasonReact web app. Nice work! We've covered a lot of new concepts, but hopefully you can already see some of the benefits of using Reason.

If you found this interesting & would like to see another post building upon this, please let me know by clicking a reaction below! ❤️ 🦄 🔖


---


## <span id="reason-app-demo">Further reading</span>

[A little more context](https://jacklewin.com/2018/getting-started-with-reason), including a link to the source code.

[Exploring ReasonML and functional programming](http://reasonmlhub.com/exploring-reasonml) - a free online book about (you guessed it) Reason and FP.

#### OSS projects

* [bs-jest](https://github.com/glennsl/bs-jest) - BuckleScript bindings for Jest.
* [lwt-node](https://github.com/kennetpostigo/lwt-node) - a Reason implementation of the Node.js API
* [reason-apollo](https://github.com/apollographql/reason-apollo) - bindings for Apollo client and React Apollo

#### Other

* [Discord channel](https://discord.gg/reasonml)
* [Forum](https://reasonml.chat)
* [Reason Town](https://reason.town) - a podcast on the ReasonML language and community
* [Redex](https://redex.github.io) - the Reason package index