|
1 |
| -# Introduction |
| 1 | +# About |
2 | 2 |
|
3 |
| -While `if/else` expressions can be used to execute conditional logic, Haskell also has a more powerful way to execute conditional logic: [pattern matching][pattern-matching]. |
4 |
| -With pattern matching, a value can be tested against one or more _patterns_. |
5 |
| -An example of such a pattern is the _constant pattern_, which matches a value against a constant (e.g. `1` or `"hello"`). |
| 3 | +When writing Haskell functions, we can make use of [pattern matching][pattern-matching]. |
| 4 | +Pattern matching is a very powerful feature of Haskell that allows you to match on data constructors and bind variables to the values inside. |
| 5 | +As the name suggests, what we do is match values against patterns, and if the value matches the pattern, we can bind variables to the values inside the pattern. |
| 6 | +Pattern matching is mainly built of these concepts: recognizing values, binding variables, and breaking down values. |
6 | 7 |
|
7 |
| -When defining functions, you can define separate function bodies for different patterns. |
8 |
| -This leads to clean code that is simple and readable. |
9 |
| -You can pattern match on any data type — numbers, characters, lists, tuples, etc. |
| 8 | +~~~~exercism/note |
| 9 | +Pattern matching in languages such as Elixir is a bit different from pattern matching in Haskell. |
| 10 | +Elixir for example allows multiple of same binding names in a pattern, while Haskell does not. |
| 11 | +~~~~ |
10 | 12 |
|
11 |
| -For example, a trivial function that takes a whole number (`Int`) and makes it _1_ closer to _0_ could be expressed like this: |
| 13 | + |
| 14 | +Take this function for example: |
| 15 | + |
| 16 | +```haskell |
| 17 | +lucky :: Int -> String |
| 18 | +lucky 7 = "Lucky number seven!" |
| 19 | +lucky x = "Sorry, you're out of luck, pal!" |
| 20 | +``` |
| 21 | + |
| 22 | +Here we have a function `lucky` that takes an `Int` and returns a `String`. |
| 23 | +We have defined two patterns for the function, one that matches the number `7` and one that matches any other number, the name can be anything (as long as it follows Haskell naming convention), but we use `x` here. |
| 24 | +If the number is `7`, the function will return `"Lucky number seven!"`, otherwise it will return `"Sorry, you're out of luck, pal!"`. |
| 25 | +What is important to note here is that the patterns are checked from top to bottom, so if we had swapped the order of the patterns, the function would always return `"Lucky number seven!"`. |
| 26 | + |
| 27 | +## List patterns |
| 28 | + |
| 29 | +A very common pattern is to match on lists, and that is taking the head and the tail of the list. |
| 30 | +This is due to lists nature of being a linked list. |
| 31 | +Here is an example of a function that returns the head and the tail of a list: |
12 | 32 |
|
13 | 33 | ```haskell
|
14 |
| -closerToZero :: Int -> Int |
15 |
| -closerToZero 0 = 0 |
16 |
| -closerToZero 1 = 0 |
| 34 | +headAndTail :: [Int] -> (Int, [Int]) |
| 35 | +headAndTail [] = error "Can't call head on an empty list" |
| 36 | +headAndTail (x:xs) = (x, xs) |
17 | 37 | ```
|
18 | 38 |
|
19 |
| -Pattern matching starts to shine when used together with other patterns, for example the _variable pattern_: |
| 39 | +We have two patterns here, one that matches an empty list and one that matches a list with at least one element. |
| 40 | +This is due to if the list is empty, we need to have a case for that, otherwise we would get a runtime error. |
| 41 | +If the list is not empty, we can match the head of the list with `x` and the tail of the list with `xs`. |
| 42 | +This is done using the `:` (cons) operator, which is used to prepend an element to a list. |
| 43 | +But in pattern matching it allows us to break down a list into its head and tail, so in a way doing the opposite. |
| 44 | + |
| 45 | +The `xs` is a common name for the tail of a list, it highlights that it is a list, but if you would be working with a nested list, you could use `xss` to highlight that it is a list of lists. |
| 46 | + |
| 47 | +## Tuple patterns |
| 48 | + |
| 49 | +As with lists, we can also match on tuples. |
| 50 | +Here is an example of a function that takes a tuple and returns the first and second element: |
20 | 51 |
|
21 | 52 | ```haskell
|
22 |
| -closerToZero :: Int -> Int |
23 |
| -closerToZero 0 = 0 |
24 |
| -closerToZero n = n - 1 |
| 53 | +sumTuple :: (Int, Int) -> Int |
| 54 | +sumTuple (x, y) = x + y |
25 | 55 | ```
|
26 | 56 |
|
27 |
| -The above example treats all inputs other than _0_ the same, and would produce incorrect results for negative numbers. |
28 |
| -This can be solved using conditional patterns, known as _guards_, which are expressed with the `|` symbol: |
| 57 | +Here we have a pattern that matches a tuple with two elements, the first element is bound to `x` and the second element is bound to `y`. |
| 58 | + |
| 59 | +## Wildcard patterns |
| 60 | + |
| 61 | +Sometimes we don't care about the value of a variable, we just want to match on the pattern. |
| 62 | +This is where the wildcard pattern comes in, it is denoted by an underscore `_`. |
| 63 | + |
| 64 | +Here is an example of a function that returns the first element of a list: |
29 | 65 |
|
30 | 66 | ```haskell
|
31 |
| -closerToZero :: Int -> Int |
32 |
| -closerToZero n |
33 |
| - | n < 0 = n + 1 |
34 |
| - | n > 0 = n - 1 |
| 67 | +head' :: [Int] -> Int |
| 68 | +head' [] = error "Can't call head on an empty list" |
| 69 | +head' (x:_) = x |
35 | 70 | ```
|
36 | 71 |
|
37 |
| -In the above examples not all possible inputs have a matching pattern. |
38 |
| -The compiler will detect this and output a warning. |
39 |
| -This is a very useful feature of Haskell that helps ensure all possible paths are covered to avoid run-time errors. |
40 |
| -It is known as _exhaustive checking_. |
41 |
| -To solve the warning, you have to handle all cases. |
42 |
| -Within _guards_ you can use the expression `otherwise` as syntactic sugar for `True` to catch all remaining patterns. |
| 72 | +Here we say we don't need the tail of the list, so we use the wildcard pattern to ignore it. |
| 73 | + |
| 74 | +## Type patterns |
| 75 | + |
| 76 | +We can also match on types, this is done by using the constructor of said type. |
| 77 | +We can also extract values from the type, like we did with tuples. |
43 | 78 |
|
44 | 79 | ```haskell
|
45 |
| -closerToZero :: Int -> Int |
46 |
| -closerToZero n |
47 |
| - | n < 0 = n + 1 |
48 |
| - | n > 0 = n - 1 |
49 |
| - | otherwise = 0 |
| 80 | +data AccountType = Guest | User String |
| 81 | + |
| 82 | +greet :: AccountType -> String |
| 83 | +greet Guest = "Welcome, guest!" |
| 84 | +greet (User name) = "Welcome, " ++ name ++ "!" |
50 | 85 | ```
|
51 | 86 |
|
52 |
| -Pattern matching will test a value against each pattern from top to bottom, until it finds a matching pattern and executes the logic associated with that pattern. |
53 |
| -**The order of patterns matters!** |
| 87 | +In the first pattern we match on the `Guest` constructor, and in the second pattern we match on the `User` constructor and bind the value inside to `name`. |
54 | 88 |
|
55 |
| -[pattern-matching]: https://learnyouahaskell.github.io/syntax-in-functions#pattern-matching |
| 89 | +[pattern-matching]: https://en.wikibooks.org/wiki/Haskell/Pattern_matching |
0 commit comments