30 minute introduction to ReasonML for React Developers
Tags: reason, tutorial, beginners, react
Published: 2020-03-23 12:48:06

---
title: 30 minute introduction to ReasonML for React Developers
published: true
description: Spending 30 minutes learning about ReasonML for React Developers
tags: reason, tutorial, beginners, react
---

The next level of React Development is with ReasonML. It allows existing Javascript developers to write OCaml code. The major benefits here are type safe inference (much more pleasant and advanced than Typescript) and really fast compilation times (orders of magnitude faster than Typescript). Not to mention is also very fun to work with.

In this article we'll try to go through as many ReasonML snippets as we can, and explain what the keywords and symbols they contain mean.

Let's get started...

### Variable Bindings

`let` introduces variable bindings. This works the same as `const` in Javascript:

```reasonml
let greeting = "Hello World"
```

`let` bindings are immutable, thus they cannot change after the first assignment:

```reasonml
let greeting = "Hello World"
greeting = "Hello Again"
^^^^^^^^
Error
```

The `let` assignment should be done immediately as the compiler needs to infer the type:

```reasonml
let greeting
greeting = "Hello Again"
^^^^^^^^
Error
```

However, using a `ref` with a wrapped value passed though allows us to assign a new value later on:

```reasonml
let greeting = ref("")
greeting = "Hello World"
```

You can create new scopes using braces `{}` and then assign the result to a binding. Everything you bind inside the scope is not available outside. The last expression evaluated is returned as the result. This is very useful for logical grouping of expressions and for better readability:

```reasonml
let fullName = {
  let first = "Theo";
  let last = "Despouds";
  first ++ " " ++ last
};
"Theo Despoudis"
```

You can bind new values to existing variables. The value of the last binding is what is referred in subsequent calculations:

```reasonml
let a = 10.;
let a = 11;
let a = a * a; // 121
```

### Type Inference

When we use let without specifying the type, the compiler will infer it:

```reasonml
let a = 10.; // float
let a = 10; // int
let a = "abc"; // string
let a = 'a' // char
```
If we want to be more explicit about the type we can declare it:

```reasonml
let a: float = 10.;
let a: int = 10;
let a: string = "abc";
let a: char = 'a';
```
You can assign a different name to a type using type aliases:

```reasonml
type statusCode = int
let notFound: statusCode = 404;
```
Note that a type name must start with a lower-case letter or an underscore. The following will fail:

```reasonml
type StatusCode = int
     ^^^^^^^^^^
let notFound: StatusCode = 404;
```

The type system of ReasonML is completely "sound" compared to Typescript which is not. See [this article](https://blog.ambrosebs.com/2018/04/07/unsoundness-in-untyped-types.html) for more information.

### Strings

Strings are wrapped in double quotes. Characters are wrapped in single quotes. Strings can span multiple lines:

```reasonml
"aaa";
"bbb;
bbb";
'a';
```

Strings are unicode encoded but chars are not. They are just ASCII so anything other than ASCII throws an error:

```reasonml
"α" // Greek letter alpha

'α';
^^^
```
### Booleans

`true` and `false` represent the `bool` type. All relevant operations that we use in Javascript work the same in ReasonML:

```reasonml
true && false;
true || true;
1 < 2;
2 >= 3;
2 == 2;
3 === 3;
```

There are no binary or xor operators. The following would not work:

```reasonml
true | true;
false & true;
true ^ true;
```

### Numbers

There are two types of numbers. Integers and floats. Float numbers end with a dot `.` whereas ints do not.

We use standard operators for integers such as `+`, `-`, `*` and `/`.

We use different operators for floats such as `+.`, `-.`, `*.` and `/.`.

```reasonml
1 + 10; // 11
1. +. 10.; // 11.

10. *. 5.; // 50.
```

We cannot mix operations between types. The following expressions will fail:

```reasonml
1 +. 10 // +. works on floats only
1. + 10; // + works on ints only
```

### Lists and Arrays

Lists and Arrays are collections of similar items. Lists are immutable and the notation is the same as Javascript:

```reasonml
let groceryList = ["eggs", "pasta", "milk"];
```

You cannot mix types:

```reasonml
let ids = [1,2, "3"];
                ^^^
```

The type of list is `list(<type>)` for example `list(int)` or `list(string)`.

There are not list methods available so you cannot do `ids.length`. Instead you need to use the [List](https://reasonml.github.io/api/List.html) module methods, for example:

```reasonml
let ids: list(int) = [1, 2, 3];
List.length(ids); // 3
let ids = List.append(ids, [4]); // [1, 2, 3, 4]
```

You can also use the spread(...) operator once to prepend items:
```reasonml
let ids: list(int) = [1, 2, 3];
let ids = [0, ...ids];
```

Note that appending does not work. You need to use `List.concat` for anything else:

```reasonml
let ids = [...ids, 4];
           ^^^^^^
```

To access a list index you need to use `List.nth` using 0-based indexing:

```reasonml
let ids: list(int) = [1, 2, 3];
let first = List.nth(ids, 0); // 1
```

Arrays are mutable collections of similar items. We surround them with `[|` and `|]` and we can use standard index notation for access:

```reasonml
let ids: array(int) = [|1, 2, 3|];
let first = ids[0]; // 1
ids[0] = 4;
// ids = [|4, 2, 3 |]
```

### Conditional Expressions

`if` and `else` are expressions (they return a value) so we can assign them to let bindings. For example:

```reasonml
let ids: array(int) = [|1, 2, 3|];

let safeFirst = if (Array.length(ids) > 0) {
    ids[0]
} else {
    0
}
// safeFirst = 1
```

You cannot have a naked `if` expression without an `else` one:

```reasonml
let ids: array(int) = [|1, 2, 3|];

let safeFirst = if (Array.length(ids) > 0) {
    ids[0]
}^^^^^^^^^^^^^
```

There is also a ternary operator just like Javascript:

```reasonml
let isLoading = false;
let text = isLoading ? "Loading" : "Submit";
```

### Records

Records in ReasonML are like Objects in Javascript. However they have stronger type guarantees and are immutable:

```reasonml
type user = {
  name: string,
  email: string
};
// Type inference here. This will only work in the same file that the user type is defined.
let theo = {
  name: "Theo",
  email: "theo@example.com"
}
```

Note that you cannot just define an object without a type:

```reasonml
let theo = {
  name: "Theo",
  ^^^^
  email: "theo@example.com"
}
```

To use a Record defined in a different file you need to prefix the type. For example if we defined the user model in `Models.re`:

```reasonml
let theo: Models.user = {
  name: "Theo",
  email: "theo@example.com"
};
```

Records are immutable:

```reasonml
type user = {
  name: string,
  email: string
};
let theo = {
  name: "Theo",
  email: "theo@example.com"
}

theo.name = "Alex"
^^^^^^^^^^^^^^^^^^
```

But you can create another Record using the spread operator:

```reasonml
type user = {
  name: string,
  email: string
};
let theo = {
  name: "Theo",
  email: "theo@example.com"
}

let theo = {
  ...theo,
  name: "Alex"
}
// {name: "Alex", email: "theo@example.com"}
```

Alternatively you can mark a field as `mutable` and perform updates:

```reasonml
type user = {
  mutable name: string,
  email: string
};
let theo = {
  name: "Theo",
  email: "theo@example.com"
}

theo.name = "Alex"
// {name: "Alex", email: "theo@example.com"}
```

You can combine different types inside a Record type using type shorthands:

```reasonml
type email = string;
type username = string;

type user = {
  email,
  username
}
```

### Functions

Functions are like es6 lambda expressions. We use parenthesis and an arrow and return a value:

```reasonml
let addOne = (n) => n + 1;
addOne(2); // 3
```

If the function spans multiple lines we can use a block scope:

```reasonml

let getMessage = (name) => {
  let message = "Hello " ++ name;
  message
}
getMessage("Theo"); // "Hello Theo" 
```

By default, function arguments are positional and the order matters. We have the option to use named (or labeled) arguments (similar to Python) using the tilde (~) operator. 

```reasonml
let getMessage = (~greeting, ~name) => {
  let message = greeting ++ " " ++ name;
  message
}
getMessage(~name="Hello", ~greeting="Theo"); // "Theo Hello"
```

However once we use one named argument we have to use all of them and not skip anything:

```reasonml
let getMessage = (~greeting, ~name) => {
  let message = greeting ++ " " ++ name;
  message
}
getMessage(~name="Hello", "Theo");
                          ^^^^^^
```

Any function with more than one argument is automatically carried:

```reasonml
let mul = (a, b) => a * b;
let times2 = mul(2);
let result = times2(3); // 6
```

Recursive functions are declared via the `rec` keyword:

```reasonml
let rec fact (n) {
  if (n === 0) {
    1
  } else {
    fact(n-1) * n
  }
}

fact(5); // 120
```

### Nulls, Optionals and Undefined

There is no `null` or `undefined` in ReasonML. Instead we have the `Option` Monad which represent either a value - `Some(value)` or no value at all - `None`:

```reasonml
let userName = Some("Alex");
let userName = None;
let userName: option(string) = Some("Alex");
```

You can use the `Belt.Option` module to perform common operations for Optionals:

```reasonml
let userName = Some("Theo");
print_string(string_of_bool(Belt.Option.isSome(userName))); // true
Belt.Option.isNone(userName); // false
```

To check is some object is null or undefined (coming from a network response for example) you can use the following API methods:

```reasonml
Js.Nullable.isNullable();
Js.eqNull();
Js.eqUndefined();
```

### Tuples

Tuples are like lists but they can contain different types of items. For example:

```reasonml
let pair = (1, "Theo Despoudis");
let pair : (int, string) = (1, "Theo Despoudis");
```

As with lists we cannot use the indexing operator `[index]`. Instead we need to use destructing to extract the i'th element. This makes tuples useful only when there are small in sized (< 3 elements):

```reasonml
let triplet = (1, "Theo Despoudis", "theo@example.com");
let (_, name, _) = triplet;  // use _ for ignoring the extracted value
name // "Theo Despoudis"
```

### Type Variants

Variants are like Union Types in Typescript. It allows us the describe an OR (|) relationship between two or more types:

```reasonml
type status =
  | NotFound
  | Error
  | Success;
  
let responseStatus = Error;
```

You can also pass the types of the arguments in some or all of the Type Names of the variant types:

```reasonml
type animalType =
  | Dog(string)
  | Cat(string)
  | Bird;

let myDog = Dog("Wallace");
```

You cannot use plain types as variants as they need to be Unique tag names or Types with a Constructor:

```reasonml
type number = int | float;
                  ^^^^^^^^
``` 

### Destructuring

We have seen destructuring before. When we have a tuple or a Record we can extract some or all of their fields to a binding:

```reasonml
type user = {id: int, name: string, email: string};
let me = {id: 1, name: "Theo", email: "theo@example.com"};
let {name, email} = me;
```

The above is just a syntactic sugar for:

```reasonml
let name = "Theo";
let email = "theo@example.com"
```

### Pattern Matching

Pattern matching is the golden fleece of Functional Programming languages. Essentially they are switch statements on steroids. For example:

```reasonml
type result =
  | OK(string)
  | NotOK(string)
  | Empty;

let response = OK("Success!");

let log =
  switch (response) {
  | OK(message) => "OK:" ++ message
  | NotOK(message) => "Error: " ++ message
  | Empty => "Nothing happened!"
  };

log // OK:Success
```

### Pipes

Pipes act as syntactic shorthand for function composition. If you have 3 functions f, g, h and you want to call them like `f(g(h(a)))` you can instead use pipes to call them like: 

```reasonml
a
 ->h
 ->g
 ->f
```

For example:

```reasonml
let userName = Some("Theo");
print_string(string_of_bool(Belt.Option.isSome(userName)));

// or

userName
    -> Belt.Option.isSome
    -> string_of_bool
    -> print_string
```

### Modules

Modules are like namespaces. We use blocks `{}` to define a module name where we can associate similar types or bindings. This aims to improve the code organisation:

```reasonml
module Arena = {
  type warriorKind =
    | Gladiator(string)
    | Hoplite(string)
    | Archer(string);

  let getName = (warriorKind) =>
    switch (warriorKind) {
    | Gladiator(name) => name
    | Hoplite(name) => name
    | Archer(name) => name
    };
};

```

Then when we need to reference a module in another file we use the module name:

```reasonml
let warrior: Arena.warriorKind = Arena.Gladiator("Brutus");
print_endline(Arena.getName(warrior)); // "Brutus"
```

For convenience we can use a shorthand for the module name using the `open` keyword ideally within it's own block scope:

```reasonml
let event = {
  open Arena;
  let warrior: warriorKind = Gladiator("Brutus");
  print_endline(getName(warrior)); // "Brutus"
};
```

### Promises

Using the [Js.Promise]()https://bucklescript.github.io/bucklescript/api/Js.Promise.html) module we can create or interact with promise objects:

```reasonml
let messagePromise =
  Js.Promise.make((~resolve, ~reject) => resolve(. "Hello"))
  |> Js.Promise.then_(value => {
       Js.log(value);
       Js.Promise.resolve("World");
     })
  |> Js.Promise.catch(err => {
       Js.log2("Failure!!", err);
       Js.Promise.resolve("Error");
     });
```

Note that we prepended a dot `.` or the [uncurry annotation](https://bucklescript.github.io/docs/en/function.html#solution-guaranteed-uncurrying) before calling resolve as the compiler will complain. This is because we want the callbacks to be uncurried.

The above will compile to the following Javascript code:

```javascript
var messagePromise = new Promise((function (resolve, reject) {
            return resolve("Hello");
          })).then((function (value) {
          console.log(value);
          return Promise.resolve("World");
        })).catch((function (err) {
        console.log("Failure!!", err);
        return Promise.resolve("Error");
      }));
```

## That's it
There are more little things to know about ReasonML but in this tutorial we explored the most common ones. Here are some further reference links for learning more about the ReasonML ecosystem:

* [ReasonML Docs](https://reasonml.github.io/docs/en/what-and-why)
* [Bucklescript Docs](https://bucklescript.github.io/en/)
* [Reason React Docs](https://reasonml.github.io/reason-react/en/)