Reason is a new programming language backed by Facebook. The language itself is really an interface to OCaml a well-known functional programming language that is strongly typed and supports native compilation. Tooling and developer tools support for OCaml is not very beginner friendly, which precludes a lot of interested developers from trying the language.

Reason, on the other hand, tries to present a much simpler syntax and compiles down to JavaScript by default to give developers a more familiar workflow. In this article, we will take a look at some of the salient features of the Reason programming language. Prior experience with OCaml will be helpful but is not necessary for understanding.

Installation

The first thing I noticed while looking at Reason was that how easy it was to get started, you can install the necessary dependencies and runtime using npm :

npm install -g bs-platform

bsb -init my-first-app -theme basic-reason

and then you can directly run the app with:

cd my-first-app

npm run build

node src/demo.bs.js

Developers coming from the node.js/JavaScript world will immediately feel a sense of familiarity with the above workflow. This is a huge relief compared to the last time a few years back when I tried to explore OCaml for web development and had to set up OPAM and Ocsigen.

The IDE and editor support for Reason is also quite good with plugins available for VS Code, IDEA, Sublime Text and Atom. I still remember the time when the only easy to use editor plugin for OCaml was OcaIDE for Eclipse and it was not that easy to setup.

Before we can start using the plugins we need to install the global binaries needed for them.

For macOS:

For Linux:

npm install -g https://github.com/reasonml/reason-cli/archive/3.0.4-bin-linux.tar.gz

Unfortunately, the Windows installation for Reason is still a bit tricky but entirely possible.

Data Types

Reason has a bunch of primitive data types that are quite familiar for those who have experience with OCaml.

Basic Types

Reason provides the usual basic types for Integers, Floats, Characters and Strings. One thing that may seem new is the use of different operators for Floats and Integers, e.g. +. and *. are used for adding and multiplying Floats.

The standard String library in Reason includes all the methods present in the OCaml Strings. And, since a Reason string maps to a JavaScript string, you can mix & match the string operations in both standard libraries. One thing to note is that Reason provides a powerful type system with tuples, variants and polymorphic types. We should avoid overloading the string type as one often does in a dynamically typed language.

Tuple Type

The tuple type (also called the product type) can help group a number of types together. For example, (int, string) denotes a pair of int and string types:

let pair: (int, string) = (2, "hello");

let v1 = fst(pair);

let v2 = snd(pair);

where, predefined functions fst and snd can be used to retrieve the first and second element of the pair.

Record Type

A record type is similar to a tuple where the fields in the record can be accessed by name instead of position. For example, we can define a point using a record type.

type point = {

x: int,

y: int,

};

We can access the individual fields in a record using the . operator:

let pt = {x: 1, y: 2};

pt.x; /* 1 */

pt.y; /* 2 */

Variant Type

The variant type is quite useful for expressing multiple things:

/* Enumerated Type */

type color = Red | Blue | Green /* Union Type */

type intorstr = I(int) | S(string) /* Recursive Data Structures */

type num = Zero | Succ(num);

type intList = Nil | Cons(int, intList);

The language also support generics via type polymorphism.

Polymorphic Type

Type polymorphism promotes code reuse, e.g. we can define a list of any arbitrary type:

type list('a) =

| Nil

| Cons('a, list('a)); type intList = list(int);

The polymorphic types are captured using type variables like 'a above. Here is another example on how to define a binary tree:

type tree('a) =

| Leaf('a)

| Node(tree('a), tree('a));

Error Values and Type

The language provides an option type that can be used for error values.

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

We can use the option type whenever we have a situation which can cause an exception. For example, we can use the option type in case of a division by zero error:

let div = (a: int, b: int) : option(int) =>

if (b === 0) {

None

} else {

Some(a / b)

};

Using the option type means that we can completely avoid the null type in Reason, and thus a pure Reason program cannot have null errors.

Expressions

Let

Let construct can be used to bind local values and functions. Let constructs can be nested and scoped:

let foo = {

let y = 1;

let z = y + 0;

y + z

};

/* y and z are not accessible here */

The let rec construct is used to create recursive function:

let rec fact = (n) =>

if (n == 0) {

1

} else {

n * fact(n - 1)

};

If-Else

Conditionals can be expressed in Reason using the If-Else construct:

let max = (x, y) =>

if (x > y) {

x

} else {

y

};

In Reason, the use of If-Else construct is not that common, since the language provides pattern matching. The conditional is just a syntactic sugar for pattern matching on boolean values.

let max = (x, y) => switch(x > y) {

| true => x

| false => y

};

Switch

Pattern matching is a powerful feature of Reason that provides a type system assisted way for deconstructing data structures. It is more concise than using conditionals or deconstruction methods.

Pattern matching in reason is provided via the switch construct, here is an example over lists:

let rec sum = (xs) =>

switch xs {

| [] => 0

| [x, ...xs] => x + sum(xs)

};

We can also match on expressions and nest patterns arbitrarily, although we should still try and flatten them as much as possible to improve readability and avoid errors. Unlike JavaScript, the switch construct in Reason is exhaustive, thus the compiler will complain if we miss a particular case.

Loop

For-loops are useful for doing imperative coding with side-effects.

let print_numbers = (n) => {

for (i in 1 to n) {

print_int(i);

print_string(" ")

};

print_newline()

};

There is a similar construct for while-loops.

while (testCondition) {

statements

};

Usually in a functional language like Reason, imperative loops are not encouraged but we can use them as needed, specially if it makes the program clearer.

Functions

Reason allows one to write both pure and impure functions. Where possible we should try and use pure functions and resort to writing impure functions only when necessary. Moreover, it is a good idea to try and keep the impure effects localised.

Labelled Arguments

If a function has multiple arguments we can use labels to refer to them. This is quite useful if the arguments are of the same type since you can refer to them by labels when calling the function:

let addCoordinates = (~x, ~y) => {

/* use x and y here */

};

...

addCoordinates(~x=5, ~y=6);

Currying

A curried function takes one argument at a time and returns a function that takes the next argument. In Reason, functions can automatically be partially called:

let add = (x, y) => x + y;

let inc = add(1);

inc(10); /* 11 */

Recursive Functions

We can define recursive (and mutually recursive) functions use the let rec construct. For example, consider the method below which is a tail-recursive version of the print_numbers method and doesn’t require the use of loops.

let print_numbers_rec = (n) => {

let rec helper = (i) =>

if (i > n) {

print_newline()

} else {

print_int(i);

print_string(" ");

helper(i + 1)

};

helper(1)

};

Advanced Features

Module

Modules can be used to encapsulate types, values and methods. OCaml has one of the most advanced module systems which is available for use with Reason.

For example, here is a module to implement stack of integers:

module StackInt = {

type e = int;

let stk = ref([]);

let push = (x: e) => stk := [x, ...stk^];

let pop = () =>

switch stk^ {

| [] => ()

| [x, ...rest] => stk := rest

};

};

Module signature and implementation are separated, which allows one to define abstract modules and provide different implementations for them. Thus, we can define a signature for a module Locations and give implementations for different kinds of Locations like Countries and Cities:

module type Locations = {

type place;

let getPlace: place => string;

}; module Countries: Locations = {

type place = India | USA ;

let getPlace = (p) => switch p {

| India => "India"

| USA => "USA"

};

}; module Cities: Locations = {

type place = Singapore | Delhi ;

let getPlace = (p) => switch p {

| Singapore => "Singapore"

| Delhi => "Delhi"

};

};

Promise

Reason has built in support for JavaScript promises via the Promise module. We can use the pipe operator |> to compose promises:

let doSomethingToAPromise = (somePromise) => {

somePromise

|> Js.Promise.then_(value => {

Js.log(value);

Js.Promise.resolve(value + 2)

})

|> Js.Promise.catch(err => {

Js.log2("Failure!!", err);

Js.Promise.resolve(-2)

})

}

JavaScript Interop

One of the key benefits of Reason is good interoperability with JavaScript. We use all libraries hosted on npm registry, and also insert arbitrary JavaScript code in a Reason program using %bs.raw.

As an example consider the jsCalculate function below, it is implemented in JavaScript. Most primitive types like int, floats, chars, strings and arrays map to the corresponding types in Reason automatically.

let jsCalculate: (array(int), int) => int = [%bs.raw

{|

function (numbers, scaleFactor) {

var result = 0;

numbers.forEach(number => {

result += number;

});

return result * scaleFactor;

}

|}

]; let calculate = (numbers, scaleFactor) => jsCalculate(Array.of_list(numbers), scaleFactor); Js.log(calculate([1, 2, 3], 10)); /* -> 60 */

In addition to the node.js and OCaml packages, there are several libraries available for use on the Reason Package Index (redex). The ecosystem around Reason is rapidly evolving as developers are starting to embrace the language for its strong typing and ease of use.

Hopefully you found the article useful and it gave you a good introduction to the Reason language. If you want to try out the language you can head over to the online interactive REPL here.