This is Part 2 of a 3 part series where I will help you make sense of ReasonML and all of its syntax and semantics. I will go through everything from basic datatypes to declaring functions in ReasonML.
Make sure that you read Part 1 and Part 2 of this series before going further. They are all connected.
Pattern matching is one of the best features of the language. It mainly does two things:
With pattern matching, we can do many great things like piping multiple patterns into a single case!
Thanks to structural equality, pattern matching works well with data structures like tuples, lists, variants, and records.
# switch (rajat) {| ("Learn ReasonML", false) => "It's Awesome"| ("Learn ReasonML", true) => "It really is awesome"};
But this will make our app exhaustive. We need to use _
to match the unmatched values for this type. Using a name instead of an underscore allows us to extract parts of the tuple.
# switch (rajat) {| (_, true) => "Its Awesome"| (text, false) => "It really is awesome. " ++ text};- string = "It really is awesome. Learn ReasonML"
In case of Lists, we can perform pattern matching on the exact list.
# switch (["a", "b", "c"]) {| ["a", "b", "c"] => true| _ => false};
The best thing about pattern matching with Lists is that ...
can be used to extract the first and last element of the List.
# switch (["x", "y", "z"]) {| [head, ...tail] => print_endline(head)| [] => print_endline("Empty list")};
When pattern matching arrays, we can only do so for arrays of specific lengths. The values in the array are matched using structural equality. We can either use an underscore or we can extract the array elements using a name.
# switch ([|"a", "b", "c"|]) {| [|"a", "b", _|] => print_endline("a, b and something")| [|_, "x", "y"|] => print_endline("something, " ++ x ++ y)| _ => print_endline("An Array")};
First, I will create a record by declaring the type and then bind it to a name
.
# type todo = { text: string, checked: bool};# let myTodo = { text: "Learn ReasonML", checked: true};
Now, I could extract the text by matching the exact value. But instead, I will try something else and extract the text using the name description.
# switch (myTodo) {| {text, checked: true} => "It is awesome: " ++ text| {text, checked: false} => "You won't regret it!" ++ text};- : string = "It is awesome: Learn ReasonML"
Lets create a variant called hero
with tags DC
and Marvel
.
# type hero = DC(string) | Marvel(string);# let dc = DC("Batman");# let marvel = Marvel("Iron Man");
Using pattern matching, I can check the tags and extract any part of the variant.
# switch (dc) {| DC(text) => "I am " ++ text| Marvel(text) => "I am " ++ text};- : string = "I am Batman"
If you want to match more than one item and return them as the result, you can do something like this:
# switch ("Batman") {| "Superman" | "Batman" | "The Flash" => "DC Comics"| _ => "Marvel Comics"};- : string = "DC Comics"
Types can also accept parameters. Parameters can be compared with generics in other languages. When you create a list, you first need to create a type list that receives a data type. Here, the data type is a type parameter.
# let rajat: list(string) = ["Batman", "Superman"];
The compiler can even inverts the type parameter of a list.
But why do we need this?
Using type parameters, we can create the a new type that can accept as many parameters as we want.
Basically, type
can turn into a function that takes in parameters and return a new type.
# type hero('a) = ('a, 'a);# let heroOne: hero(string) = ("Superman", "Clark Kent");
This way we can avoid repetition while creating more types.
Also note that the type parameters are only valid if they start with a “`“, followed by a character or word.
let
bindings are immutable by default. Once a binding refers to a value, it cannot refer to anything else. But you can circumvent this issue by creating a new binding that has the same name and shadows the previous binding.
# let person = "Clark Kent";# print_endline(person); /* Prints "Clark Kent" */# let person = "Superman";# print_endline(person); /* Print "Superman" */
There is also a way to turn let
bindings mutable by using a reference to wrap the actual value.
# let foo = ref(5);let foo: ref(int) = {contents: 5};
Now if I want to change the data inside of foo
, this is how I will do it:
# foo := 6;- : unit = ()# foo;- : ref(int) = {contents: 6}
The value of foo
is changed from 5 to 6. To retrieve the value of reference, I need to use ^
character.
# foo^;- : int = 6
In Reason, Exception is a kind of variant that you get when trying to find something inside an empty list.
# List.find(x => x == "Rajat", []);Exception: Not_found.
That’s not all! You can create your own exceptions using the raise
function. To catch exceptions in your app, use pattern matching.
# raise(Not_found);Exception: Not_found.# try (raise(Not_found)) {| Not_found => "Oh Oh!"};= : string = "Oh Oh!"
We can directly match exceptions in a switch
expression using exception
.
# switch (List.find(x => x == "rajat", [])) {| name => "Obtained"| exception Not_Fount => "Not found"};
: string = "Not found"# switch (List.find(x => x == "rajat", ["rajat"])) {| item => "Obtained"| exception Not_found => "Not found"};- : string = "Obtained"
There are not many uses for exception
. In fact, you can instead use option
.
for
loops are used to iterate from the first to the last value of a data structure.
# for (x in 1 to 5) {print_int(x * 2);print_string(" ");};2 4 6 8 10 - : unit = ()
The range needs to be valid and go from a lower value to a higher value. If you try to do it the other way, for
will not do anything. To make for
work in the reverse direction, replace to
with downto
.
The while
loops will keep operating as long as the condition given is true
.
# let x = ref(0);let x: ref(int) = { contents: 0};# while (x^ < 5) {print_int(x^);x := x^ + 1;};01234- : unit = ()
Modules can be described as small blocks in your code. They allow us to encapsulate things like let
bindings and types
into logical entities.
Every file in Reason is a module. This is why name
needs to be unique in each project.
Use the module
keyword to create a modules in your Reason project. Make sure that the name of your module is capitalized.
# module Rajat = {};`module Math` : { };# module Math = {let name = "rajat";let age = 24;};`module Math`: { let name: string; let age: int };
To access anything that is inside a module, use the .
notation.
# Rajat.name;- : string = "rajat"# Rajat.age;- : int = 24;
.
notation is really helpful here as we can access types stored inside the module as well. This way, the compiler will only look at the current module or its parent module.
Reason also allows us to open a module’s definition and refer to its contents without prepending the name.
To do so, we use the open
keyword. This will open the module globally and import its definition in the current scope.
Back in Part 1 of this series, I had said that Reason can be compiled into JavaScript using something called BuckleScript. Let’s see how this is actually done.
First we need to install BuckleScript into our system.
npm install -g bs-platform
With this package installed, I can not only compile my code into JavaScript, but also into native binary code.
Now I need to set up a new project using bs-platform
bsb -init reason-project -theme basic-reason
This command will create a new directory called reason-project
in your system. If you take a look at the contents of this directory, you will notice that it shares a few similarities with a typical JavaScript project.
The only unique thing here, is the bsconfig.json
file. This file is where we can configure the BuckleScript for this project.
If you look at the src
directory of this project, you will see that it contains a Demo.re
file. Let’s delete it and create a new file named Main.re
. You can write any ReasonML code here and run the build
script using NPM/Yarn. This will compile any file with .re
extension into a matching JavaScript file. So our Main.re
file will compile into Main.bs.js
.
You can run this JavaScript file using node
.
node src/Main.bs.js
You can also using start
script to build it manually. This script will watch for file changes and compile accordingly.
This series should be enough to get you started with ReasonML. But if you still want to know more, take a look at Reason’s official Docs here:
Quickstart · Reason_sh_reasonml.github.io
You can also take a look at Nik Graf’s Course on Egghead. It helped me a lot!
Get Started with Reason_This course foremost will teach you about the syntax and semantics of the programming language Reason or also referred…_egghead.io
I am Rajat S, Technical Content Writer at GeekyAnts. Aspiring Coder who has a long way to go. A Die-Hard DC Comics Fan who loves Marvel Movies. Known for multi tasking.
Thanks for reading, and hopefully this was helpful! Please 👏 if you liked this post and follow me here and/or on Twitter to stay updated about new posts from me!