Pattern Matching
This is the mechanism of checking a value against a certain pattern.
We have encountered pattern matching in the previous lessons, like in the Variants lesson we had a pattern match in the schedule function to map out our tasks for each specific day.
Just to Recap
```reason
type days =
| Sun
| Mon
| Tue
| Wed
| Thur(int)
| Fri
| Sat
let schedule = day => switch day {
| Sun => "Relax and chill"
| Mon => "Sleep on your way to work"
| Tue => "Go out for a movie"
| Wed => "Learn a new Skill"
| Thur(miles) => "Run in the morning for " ++ string_of_int(miles) ++ " miles"
| Fri => "Plan for the following week"
| Sat => "Run in the evening"
};
```
Pattern matching on Basic data types.
IntegersLet's imagine that you are an athlete and you want to have an app that tells you how many laps you are left with in a particular Olympics race.
We can have the following match
```reason
let lapsLeft = (totalLaps,lapsRan) => switch lapsRan {
| 0 => "You are just beginning the race " ++ string_of_int(totalLaps) ++ " Left!!!"
| 1 => "Finished your first lap " ++ string_of_int(totalLaps - lapsRan) ++ " Left"
| _ => "Keep pushing " ++ string_of_int(totalLaps-lapsRan) ++ " Left"
};
```
You can match as many integers as you want.
NoteYou cant match on every possible integer so be sure to include a fall_through to allow integers that haven’t been matched to go to that branch. (_)
ListsRecall the recursive function that we used to get the sum of a all items of a list
```reason
let rec sum = lst => {
switch lst {
| [] => 0
| [hd, ...tl] => hd + sum(tl)
};
}
```
In the above case , we are simply matching an empty list to 0 and a list with a head and tail to the sum of the list's head and recursive call to the tail
NoteYou can pattern match on anything. strings, Variants, floats etc.
Pattern matching helps us cover all cases in which a value can range from
Try rewriting the sum function as follows
```reason
let rec sum = lst => {
switch lst {
| [hd, ...tl] => hd + sum(tl)
};
}
```
You'll instantly see the compiler complain with the following error.
`Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]`
Thanks to Pattern Matching, we can never forget to include certain matches that our code can branch to.
When Clauses.
Lets say we want to classify someone as either young or old. We want to pass in an age to a function as a parameter and expect back a string telling us whether the person is old or young.
```reason
let ageJudge = age => switch age {
| 0 => "Very young"
| 10 => "Young"
| 20 => "Youth"
| 30 => "Middle"
| 40 => "Middle Old"
| 50 => "Old"
| _ => "Undecided"
};
```
This doesn't display any errors but, there are a lot of ages that will go to the fall_through branch.
In order to solve this issue we use a when clause. Check out the code below. In other languages like Erlang, the when clause is called a guard.
```reason
let ageJudge = age => switch age {
| young when young <= 20 => "Young"
| _ => "Old"
};
```
Anything can be matched, Even exceptions.
Lets say for example we were to classify people based on their eyes, if you have one eye, you are a pirate, if you have 2 eyes you are a normal human, 4 you have glasses because of poor vision, the rest, we raise an exception. For instance we raise an exception for 3 or 12
Here's how we pattern match for that
```reason
exception Impossible(string);
let eyeOcupation = eyescount => switch eyescount {
|0 => "You may be blind"
| 1 => "You are a pirate"
| 2 => "You are a normal human with perfect eye sight"
| 4 => "You have poor vision, dont loose your glasses"
| _ => raise(Impossible("You Cannot have that many eyes"))
};
let printEyes = eyes => {
switch (eyeOcupation(eyes)) {
| eyes => print_endline("Your status is " ++ eyes)
| exception Impossible(errorMessage) => print_endline("Something went wrong : " ++errorMessage)
};
}
```
First, we create our own exception
```reason
exception Impossible(string)
```
Its a variant as you might have guessed that takes a string as an argument.
We are doing 2 pattern matching, One for the eyeOcupation and the other for printing the eyes out.
```reason
let eyeOcupation = eyescount => switch eyescount {
|0 => "You may be blind"
| 1 => "You are a pirate"
| 2 => "You are a normal human with perfect eye sight"
| 4 => "You have poor vision, dont loose your glasses"
| _ => raise(Impossible("You Cannot have that many eyes"))
};
In the `eyeOcupation` function we only care for 0 | 1 | 2 | 4 , the rest we throw an exception
```
In the `eyeOcupation` function we only care for 0 | 1 | 2 | 4 , the rest we throw an exception
```reason
| _ => raise(Impossible("You Cannot have that many eyes"))
```
In the other next function.
```reason
let printEyes = eyes => {
switch (eyeOcupation(eyes)) {
| eyes => print_endline("Your status is " ++ eyes)
| exception Impossible(errorMessage) => print_endline("Something went wrong : " ++errorMessage)
};
}
```
We match on both the string and the exception and print different strings based on the case that was matched.
Patterns are everywhere, and when pattern matching is used, we can ascertain that our code will be more clear and concise.