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.
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.