Welcome back to our tutorial covering the fundamentals of ReasonML. Yesterday we covered some of the more interesting portions of the ReasonML language. Specifically, we looked at some of the data structures along with the basics of how functions work in ReasonML. Now, we're going to move forward with a few more language fundamentals so that tomorrow we can transition to playing with the standard library.

ReasonML Fundamentals

So to get things started, we're going to first turn our attention to destructuring. To do this, let's first write some code that can illustrate what we're getting into.

/* destructuring */
let (numA: int, numB: int) = (1, 2);

let someInts = (1, 2);
let (numC: int, numD: int) = someInts;


type dog = {
  name: string,
  breed: string

let rover: dog = {name: "Rover", breed: "Great Dane"};
let {name, breed} = rover;


So what exactly is going on here? Well, if you're coming from the ES8 version of Javascript you may be chomping at the bits here ready to explain what is going on. For those who haven't, it isn't really too bad to explain. Let's start here at the top. We have the most raw form of destructuring. In this case we're using two tuples. The one on the left is being used to essentially declare our variable names and types while the tuple on the right is going to carry the values we assign being assigned to the corresponding names on the left. If that was confusing, just look here at the Js.logs then turn your attention down here to the console output and you will see the full story.

We can see that numA now equals 1 and numB equals 2. This gets us down to the nuts and bolts of what destructuring in this case is really about. It allows us a more efficient and elegant way of assigning lots of values (variables in other languages).

If we turn our attention here, we can see this being displayed again. It's not rocket science.

Knowing this. It's obviously worth addressing the point that destructuring doesn't only apply to tuples. We can see here that we are also applying destructuring to a record. In this case we extract the values from our Dog named Rover and bind them to name and breed. Not so bad.

However the rabbit hole doesn't end there. Before we go too deep. It is worth discussing that ReasonML has large support for pattern matching in general. It may not be an explict "feature" in the same vein as a record or variant, but it absolutely persists throughout the language. However, time is of the essence and links will be provided for those who wish to dive deeper into ReasonML's pattern matching capabilities. Now we need to take a quick look at imperative loops.

Imperative loops may not necessarily be a normal part of a functional language but ReasonML does provide them for those use cases where nothing else will do. Let's take a quick look at how this will look.

let z = [1, 2, 3, 4, 5];
for(item in List.nth(z, 0) to List.nth(z, List.length(z) - 1)) {

As you can see, this isn't a particularly out there set up. We have our list, then we iterate through the list and print everything to the screen. It isn't rocket science. We'll revisit this again when we go through the standard library because yes, I do realize there are a few commands that may be a little unfamiliar here with the List.nth.

Now it's time to move forward and put our focus on another key facet of working with ReasonML and that is modules. For many, especially those with a Javascript background, this can be one of the most confusing aspects to transition to Reason. When defined within a file, a module acts like a file within your file. You can write all sorts of code within it just like you would any other code in a file. To better grasp this, let's try writing a quick example.

module SimpleCalculator {
  let add = (x, y) => x + y;
  let subtract = (x, y) => x - y;
  let multiply = (x, y) => x * y;
  let divide = (x, y) => x / y;

  let someNumber = 3;

let myNumber = SimpleCalculator.add(1, 2);
let blah = SimpleCalculator.someNumber;

As you can see, this isn't particularly complicated. All of the functions and values you have defined are available outside the module using dot notation. Useful if you want to organize certain functionalities into their own namespaces.

However, now it's time to get to the part where it becomes a little mind bending for many developers. All ReasonML files are modules and all of the content you write within can and will be available to any other ReasonML files within your project by default. For Javascript developers, this will feel odd because we are accustomed to explicitly declaring which values and functions will be exported or even imported in our files via CommonJS or ES8 modules. So knowing this, you will eventually notice that when you are examining code from one of the ReasonML examples provided on the home site, you don't see imports or exports. All of the files are already able to access each other as needed. To better grasp this. Imagine you have two files, and When compiled and run in a project, they already can access each others needed functions and values through the same dot notation we used when we wrote our module earlier. Having a hard time comprehending this? It's okay, I'm a Javascript developer myself, so this was a bit tricky to accept. Just know that if you were in right now and wanted to access some functionality from it would look like this.

let x: string = Home.someValue;

That's it. It would work the same way vice versa from the Home file. Just remember, all ReasonML files are modules. It's also key to understand that because of this nature, all ReasonML files will act on a flat structure. You can organize the files in a series of folders however you like but when compiled they will always be treated and located as a flat structure. This means all file names must be unique.


So this should cover the basic fundamentals for now. Tomorrow, we will shift our attention to working with the standard library and seeing what all it has to offer us. We will only be scratching the surface but it should be enough to get off the ground.

Helpful Links