|
| 1 | +# Introduction |
| 2 | + |
| 3 | +Suppose you are on Mercury. |
| 4 | +The [orbital period][wikipedia-orbital-period] of Mercury is approximately one fourth of an Earth year. |
| 5 | +This means that in one Earth year, Mercury will complete about four whole orbits around the sun. |
| 6 | +As a consequence, your 'age' on Mercury is about four times your age on Earth. |
| 7 | + |
| 8 | +More generally, your 'age' on a certain planet is the number of seconds since your birth, divided by the number of seconds it takes the planet to orbit the sun once. |
| 9 | + |
| 10 | +The various planets' orbital periods as compared to the Earth's are given. |
| 11 | +To convert them from _Earth years_ per orbit to _seconds_ per orbit you need to multiply this by the number of seconds in one Earth year. |
| 12 | + |
| 13 | + |
| 14 | +## Approach: give meaningful names to important values |
| 15 | + |
| 16 | +```haskell |
| 17 | +ageOn :: Planet -> Float -> Float |
| 18 | +ageOn planet seconds = seconds / (periodInEarthYears * secondsPerEarthYear) |
| 19 | + where |
| 20 | + secondsPerEarthYear = 60 * 60 * 24 * 365.25 |
| 21 | + |
| 22 | + periodInEarthYears = case planet of |
| 23 | + Mercury -> 0.2408467 |
| 24 | + Venus -> 0.61519726 |
| 25 | + Earth -> 1 |
| 26 | + Mars -> 1.8808158 |
| 27 | + Jupiter -> 11.862615 |
| 28 | + Saturn -> 29.447498 |
| 29 | + Uranus -> 84.016846 |
| 30 | + Neptune -> 164.79132 |
| 31 | +``` |
| 32 | + |
| 33 | +This approach uses a `case` expression to choose the relevant orbital period. |
| 34 | +Also a `where` clause is used to give names to important values. |
| 35 | +This tends to greatly improve readability. |
| 36 | + |
| 37 | + |
| 38 | +## General guidance |
| 39 | + |
| 40 | +### `where` clauses are your friend! |
| 41 | + |
| 42 | +Giving meaningful names to subexpressions can do wonders for code readability. |
| 43 | +`let` expressions allow the same. |
| 44 | +Being expressions – which `where` clauses aren't – `let` expressions are a bit more flexible in their use. |
| 45 | +However, `where` clauses list the local definitions _after_ the main expression. |
| 46 | +This allows you to paint the broad strokes of your strategy first, and to fill in the details later. |
| 47 | +This is so convenient that it amply compensates for `where` clauses not being expressions. |
| 48 | + |
| 49 | +More on `where` and `let` elsewhere: |
| 50 | + |
| 51 | +- Haskell Wiki: [Let vs. Where][haskellwiki-let-vs-where] |
| 52 | +- Haskell Wikibook: |
| 53 | + - [`where` clauses][wikibook-where] |
| 54 | + - [`let` bindings][wikibook-let] |
| 55 | + - [`let` and `where` revisited][wikibook-let-vs-where] |
| 56 | + |
| 57 | + |
| 58 | +### `case` expressions are also your friend! |
| 59 | + |
| 60 | +Many beginning Haskellers write code like |
| 61 | + |
| 62 | +```haskell |
| 63 | +ageOn planet seconds |
| 64 | + | planet == Mercury = _ |
| 65 | + | planet == Venus = _ |
| 66 | + | {- etc. -} = _ |
| 67 | +``` |
| 68 | + |
| 69 | +Using guards like this is an [anti-pattern][wikipedia-anti-pattern]. |
| 70 | +Pattern-match instead, for example using a `case` expression: |
| 71 | + |
| 72 | +```haskell |
| 73 | +ageOn planet seconds = |
| 74 | + case planet of |
| 75 | + Mercury -> _ |
| 76 | + Venus -> _ |
| 77 | + {- etc. -} -> _ |
| 78 | +``` |
| 79 | + |
| 80 | +Pattern matching is a fundamental concept in Haskell, but this document is too short to be able to fully explain it. |
| 81 | +Please consult your other learning resources. |
| 82 | +The track docs include an article on [Haskell learning resources][learning-resources]. |
| 83 | + |
| 84 | +Pattern matching with `case` has benefits over using guards: |
| 85 | + |
| 86 | +- `case` expressions are _expressions_. |
| 87 | + Therefore as pieces of code they are very easy to move around during code composition. |
| 88 | + You can give them names (e.g. in a `where` clause) and also pass them to functions as arguments. |
| 89 | +- The compiler is able to check that you handle all possible cases. |
| 90 | + If you overlook some cases and use guards, the compiler will not help you. |
| 91 | + But if you are pattern matching it will! |
| 92 | +- When you use pattern matching, the compiler can use its understanding of your code to apply code transformations that improve performance. |
| 93 | + |
| 94 | + |
| 95 | +[learning-resources]: |
| 96 | + https://exercism.org/docs/tracks/haskell/learning |
| 97 | + "How to learn Haskell" |
| 98 | + |
| 99 | + |
| 100 | +[haskellwiki-let-vs-where]: |
| 101 | + https://wiki.haskell.org/Let_vs._Where |
| 102 | + "Haskell Wiki: Let vs. Where" |
| 103 | +[wikibook-let-vs-where]: |
| 104 | + https://en.wikibooks.org/wiki/Haskell/More_on_functions#let_and_where_revisited |
| 105 | + "Haskell Wikibook: let and where revisited" |
| 106 | +[wikibook-let]: |
| 107 | + https://en.wikibooks.org/wiki/Haskell/Next_steps#let_bindings |
| 108 | + "Haskell Wikibook: let bindings" |
| 109 | +[wikibook-where]: |
| 110 | + https://en.wikibooks.org/wiki/Haskell/Variables_and_functions#where_clauses |
| 111 | + "Haskell Wikibook: where clauses" |
| 112 | +[wikipedia-anti-pattern]: |
| 113 | + https://en.wikipedia.org/wiki/Anti-pattern |
| 114 | + "Wikipedia: Anti-pattern" |
| 115 | +[wikipedia-orbital-period]: |
| 116 | + https://en.wikipedia.org/wiki/Orbital_period |
| 117 | + "Wikipedia: Orbital period" |
0 commit comments