Intro

Welcome back to our ReasonML tutorial series. Yesterday we got started on learning the ins and outs of the ReasonML language fundamentals. Today, we're going to continue with this by picking up precisely where we left off. Let's get started.

ReasonML Fundamentals

First off for today, we're going to get accustomed to a unique data type in the ReasonML language called records. For those coming from Javascript, you'll find these are similar to standard Javascript objects however they do have their differences which we will go into in just a minute. First, let's write some code for us to work with.

/* record type */
type aPerson = {
    age: int,
    name: string
};
/* record value */
let misha: APerson = {
    age: 31,
    name: "Misha"
};

let mark: APerson = {
    age: 31,
    name: "Mark"
};

Js.log(misha.name);
Js.log(mark.name);

First, we need to take a look at the type APerson. Here we are defining what our APerson data type will look like. You'll see that it is like a Javascript object only we're predefining what kind of values will go where in our record. This is a required step if we want to ReasonML records. Once we have this, we then have our two record values that we generated based on our APerson type. Not too bad. It is also worth noting that yes, ReasonML is capable of inferring the data type for these record values. However, this is where I become opinionated and say that this is a static typed language so we're going to type this as a static typed language. The end.

It is also key to note when discussing records that they have other differences from Javascript objects. First, as tends to be the case, records are immutable by default. Also field names and types are fixed. You can't go changing things around on a whim. Beyond this, we could get into performance benefits but that will be for another time. Just know that these are records and they work.

Now it's time to get into one of the biggest features of ReasonML and that is the variant data type. Before we go too far, let's write out an example.

/* variant */
type myVariant =
  | Yes
  | No
  | Maybe;

let myThingamajig: myVariant = Yes;

let myMessage: string =
    switch myThingamajig {
    | Yes => "Woot"
    | No => "Aww"
    | Maybe => "Huh?"
    };

type socialMedia =
  | FaceBook(string, int)
  | Instagram(string);

let myAccount: socialMedia = FaceBook("Jon", 30);
let anotherAccount: socialMedia = Instagram("Maggie");

So first things first, I know there is some extra code but let's put our focus here. We have our variant defined so what exactly is it? Well, a variant operates like a collection of enums. These values are not strings nor any other particular data type. Instead we would just call them constructors. Also notice we are using the pipe symbol to indicate each of our constructors. Now, why are these constructors relevant at all if they aren't a string, int, or any other of our normal data types? Well, let's take a look down here. We have myThingamajig and it is defined as Yes from our constructor list. Now it's time to see the relevance of these constructors. Have a look at this switch statement. Here, we can use our constructors as a way of defining our cases and what action needs to be taken when case occurs.

It's also worth noting here that we can also pass in other values and conditions to our constructors. Not bad. We'll be seeing more of this later but it is time to move forward.

Next, we're going to take a look at two more familiar data types. Lists and arrays. Let's write out some code.

/* list */
let aList = [1, 2, 3, 4];

/* array */
let anArray = [|1, 2, 3, 4|];

Good. First, let's look at our list. First thing to note is that our list is immutable, only supports one data type at a time, and is fast a prepending items. Our array on the other hand is more like a Javascript array. It is mutable, fast at random access and updates, and flexibly sized when compiled to Javascript (it's fixed sized when compiled to native). Beyond that, there isn't too much else to see here so let's move forward.

Now we need to take a look at functions. Let's buckle down because we have some code to write.

let add = (x, y) => x + y;
let subtract = (x, y) => {
  x - y;
};
let multiply = (~first as x, ~second as y) => x * y;
let divide = (~first as x=12, ~second as y=3) => x / y;

As you can see here, syntactically ReasonML's functions are almost identical to Javascript's and this was done on purpose. For most people who have any familiarity with Javascript's arrow functions. You already know what this is. The only thing that may catch you off guard will be here where were are aliasing our arguments. This is useful when seeing the code preview for your function in an editor this way you can better remember which argument does what while keeping the internal code for your function clean. We also can set up predefined values. Pretty nice.

Lastly, let's take a quick look at if/else statements.

if(1){
    print_newline("hello world");
};

if (1) {
    print_newline("hello world");
} else {
    print_newline("hello maggot");
}

Again, not bad at all. This is no different than the usual if/else statement you would see anywhere else.

Conclusion

So we've made it further into learning about ReasonML's fundamental language. However, we still have more that we need to get into. Tomorrow, we will focus on in the last portion of the language fundamentals before we begin learning about the standard library and maximizing much of what ReasonML gives us by default.

Alex Allen

Alex is an independent developer who is obsessed with both performance and information security. When not writing code, Alex is either playing guitar or working in his garden.

  1. Comments for ReasonML Fundamentals Part 2

You must login to comment

You May Also Like