Home/Tutorials/ReasonML/Reason Modules
ReasonML5 min read

Reason Modules

Modules are like files. They contain let bindings, functions types… etc

Modules


Modules are like files. They contain let bindings, functions types… etc

Take a look


/* mods.re */

module Laptop = {
    type laptop = Hp | Lenovo | MacbookPro | Asus | Dell

    let mylaptop = Dell;

    let message = switch mylaptop {
    | Dell => "Hello Dell User"
    | (Hp | Lenovo) => "Pleased to meet you."
    | (Asus | MacbookPro) => "There's VIP seats on the front row"
    };
};

let wishLaptop = Laptop.MacbookPro

You can also access their properties like what the wishLaptop let binding is doing.

Well the wishLaptop let binding is still in the same file as the Laptop module, but even if it were not, we would still access the module’s properties using the dot notation.


/*campus.re*/

let acceptedLaptops = (laptop: Mods.Laptop.laptop) => switch laptop {
| Hp => "Acccepted"
| (Dell | Asus) => "Accepted"
| (MacbookPro) => "Recommended"
| _ => "Not recognised by the School"
};

(It’s the school that doesn’t recognise the Lenovo brand. Not me :) )

As you can see modules are created with a capital letter. and files are modules. Take a look at the campus.re acceptedLaptops function arguements. We know the laptop param is of type laptop, but laptop is inside module Laptop and the Laptop module is inside the Mods module.


Mods -> Laptop -> laptop

This is quite a bit of boilerplate code. What we can do is open up a module.

Lets see what that means by opening up the Mods module.


open Mods

let acceptedLaptops = (laptop: Laptop.laptop) => switch laptop {
| Hp => "Acccepted"
| (Dell | Asus) => "Accepted"
| (MacbookPro) => "Recommended"
| _ => "Not recognised by the School"
};

When you open up a module, you dont have to type its name to get its properties.

Lets take this even further by opening up the Laptop module.


open Mods
open Laptop

let acceptedLaptops = (laptop: laptop) => switch laptop {
| Hp => "Accepted"
| (Dell | Asus) => "Accepted"
| (MacbookPro) => "Recommended"
| _ => "Not recognised by the School"
};

Not much boilerplate now :).

Okay: Lets extend this example even further.

So let’s say the school saw that people buy a lot of Lenovo laptops without knowing that its not recommended for reasons it wont say, so it entrusts a shop with the responsibility of notifying students if the laptop is recommended or not once they visit it to buy the laptop.

So once a student picks a laptop it is passed through a scan to see whether the brand is accepted by the school.

Here’s what the scan looks like


/* shop.re**/

open Mods.Laptop

let laptopPicked = Hp

let isLaptopAccepteable = switch (Campus.acceptedLaptops(laptopPicked)) {
| "Accepted" => true
| "Recommended" => true
| _ => false
};

Recall the acceptedLaptops function returns “Accepted” | “Recommended” and “Not recognised…”

So if the laptop you picked is recommended or accepted you get a pass and buy that one, if not a false is returned and you have to pick another brand.

Notes

You can open nested Modules on the same line.


open Mods.Laptop

Okay: One more thing.

A laptop can be accepted by the school but the student may not be willing to buy it if it doesn’t have a backlit keyboard.

What we can do is create another module inside of mods.re for LaptopSpecs

Here’s how that looks like


module LaptopSpecs = {
    open Laptop

    let hasBackLit = (laptop: laptop) => switch laptop {
    | MacbookPro => true
    | Dell => true
    | (Hp | Lenovo) => false
    | Asus => false
    };
}

And back to the shop, we can create a function that will ascertain the user buying the laptop as a result of being accepted by the school and also having a backlit keyboard.


/** shop.re*/
open Mods.Laptop
open Mods.LaptopSpecs

let laptopPicked = Hp

let isLaptopAccepteable = switch (Campus.acceptedLaptops(laptopPicked)) {
| "Accepted" => true
| "Recommended" => true
| _ => false
};

let willStudentBuy = switch isLaptopAccepteable {
| true => switch (hasBackLit(laptopPicked)) {
| true => true
| false => false
};
| false => false
};

Now we know that a user will buy a laptop if and only if its accepted by the school and if it has a backlit keyboard.

But on looking at the code, there’s a lot of open statements from the same file and this can confuse us especially if we add on more modules to the file.

What we can do is copy all the props of one module down to another and only open that Module with its attributes and the copyied attributes. (Like inheritance)

We use the include keyword for this.


module LaptopSpecs = {
    include Laptop

    let hasBackLit = (laptop: laptop) => switch laptop {
    | MacbookPro => true
    | Dell => true
    | (Hp | Lenovo) => false
    | Asus => false
    };
}

include is different from open in that it copies over the props of one module and then opens whereas open brings a module’s content the current scope, so that you don’t have to refer to a value by prefixing it with the module’s name.

So with that


/* shop.re */

/* open Mods.Laptop */
open Mods.LaptopSpecs
we can comment out the opening of the Laptop module.

INTERACTIVE SIMULATION CHALLENGE+250 XP
Workbench Verification

Ready to test "Reason Modules" in production?

See how ReasonML algorithms and server loops perform under 100k req/s traffic spikes.

Simulator: SysDesign Ops Simulator