Skip to content

Commit a03511e

Browse files
authored
rewrite guard example to use density (of substances) instead of BMI (learnyouahaskell#26)
1 parent 3dc9db8 commit a03511e

File tree

1 file changed

+19
-19
lines changed

1 file changed

+19
-19
lines changed

docs/syntax-in-functions.html

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -167,33 +167,33 @@ <h1 style="margin-left:-3px">Syntax in Functions</h1>
167167
<a name="guards-guards"></a><h2>Guards, guards!</h2>
168168
<img src="https://s3.amazonaws.com/lyah/guards.png" alt="guards" class="left" width="83" height="180">
169169
<p>Whereas patterns are a way of making sure a value conforms to some form and deconstructing it, guards are a way of testing whether some property of a value (or several of them) are true or false. That sounds a lot like an if statement and it's very similar. The thing is that guards are a lot more readable when you have several conditions and they play really nicely with patterns.</p>
170-
<p>Instead of explaining their syntax, let's just dive in and make a function using guards. We're going to make a simple function that berates you differently depending on your <a href="http://en.wikipedia.org/wiki/Body_mass_index">BMI</a> (body mass index). Your BMI equals your weight divided by your height squared. If your BMI is less than 18.5, you're considered underweight. If it's anywhere from 18.5 to 25 then you're considered normal. 25 to 30 is overweight and more than 30 is obese. So here's the function (we won't be calculating it right now, this function just gets a BMI and tells you off)</p>
170+
<p>Instead of explaining their syntax, let's just dive in and make a function using guards. We're going to make a simple function that responds differently depending on the <a href="https://en.wikipedia.org/wiki/Density">density</a> given. Density (or specific mass) is a substance's mass per unit of volume (here, grams per liter). If a substance has a density of less than 1.2, it will float in air, as 1.2g/L is the density of air. If it has more than 1000g/L (the density of water), it will sink in water. Between are things (like people, usually) that will neither float away nor sink in water.
171+
172+
So here's the function (we won't be calculating density right now, this function just gets a density and responds)</p>
171173
<pre name="code" class="haskell:hs">
172-
bmiTell :: (RealFloat a) =&gt; a -&gt; String
173-
bmiTell bmi
174-
| bmi &lt;= 18.5 = "You're underweight, you emo, you!"
175-
| bmi &lt;= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
176-
| bmi &lt;= 30.0 = "You're fat! Lose some weight, fatty!"
177-
| otherwise = "You're a whale, congratulations!"
174+
densityTell :: (RealFloat a) =&gt; a -&gt; String
175+
densityTell density
176+
| density &lt; 1.2 = "Wow! You're going for a ride in the sky!"
177+
| density &lt;= 1000.0 = "Have fun swimming, but watch out for sharks!"
178+
| otherwise = "If it's sink or swim, you're going to sink."
178179
</pre>
179-
<p>Guards are indicated by pipes that follow a function's name and its parameters. Usually, they're indented a bit to the right and lined up. A guard is basically a boolean expression. If it evaluates to <span class="fixed">True</span>, then the corresponding function body is used. If it evaluates to <span class="fixed">False</span>, checking drops through to the next guard and so on. If we call this function with <span class="fixed">24.3</span>, it will first check if that's smaller than or equal to <span class="fixed">18.5</span>. Because it isn't, it falls through to the next guard. The check is carried out with the second guard and because 24.3 is less than 25.0, the second string is returned.</p>
180+
<p>Guards are indicated by pipes that follow a function's name and its parameters. Usually, they're indented a bit to the right and lined up. A guard is basically a boolean expression. If it evaluates to <span class="fixed">True</span>, then the corresponding function body is used. If it evaluates to <span class="fixed">False</span>, checking drops through to the next guard and so on. If we call this function with <span class="fixed">24.3</span>, it will first check if that's smaller than or equal to <span class="fixed">1.2</span>. Because it isn't, it falls through to the next guard. The check is carried out with the second guard and because <span class="fixed">24.3</span> is less than <span class="fixed">1000.0</span>, the second string is returned.</p>
180181
<p>This is very reminiscent of a big if else tree in imperative languages, only this is far better and more readable. While big if else trees are usually frowned upon, sometimes a problem is defined in such a discrete way that you can't get around them. Guards are a very nice alternative for this.</p>
181182
<p>Many times, the last guard is <span class="fixed">otherwise</span>. <span class="fixed">otherwise</span> is defined simply as <span class="fixed">otherwise = True</span> and catches everything. This is very similar to patterns, only they check if the input satisfies a pattern but guards check for boolean conditions. If all the guards of a function evaluate to <span class="fixed">False</span> (and we haven't provided an <span class="fixed">otherwise</span> catch-all guard), evaluation falls through to the next <em>pattern</em>. That's how patterns and guards play nicely together. If no suitable guards or patterns are found, an error is thrown.</p>
182-
<p>Of course we can use guards with functions that take as many parameters as we want. Instead of having the user calculate his own BMI before calling the function, let's modify this function so that it takes a height and weight and calculates it for us.</p>
183+
<p>Of course we can use guards with functions that take as many parameters as we want. Instead of having the user calculate the density of the substance on their own before calling the function, let's modify this function so that it takes a mass (in grams) and volume (in liters).</p>
183184
<pre name="code" class="haskell:hs">
184-
bmiTell :: (RealFloat a) =&gt; a -&gt; a -&gt; String
185-
bmiTell weight height
186-
| weight / height ^ 2 &lt;= 18.5 = "You're underweight, you emo, you!"
187-
| weight / height ^ 2 &lt;= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
188-
| weight / height ^ 2 &lt;= 30.0 = "You're fat! Lose some weight, fatty!"
189-
| otherwise = "You're a whale, congratulations!"
185+
densityTell :: (RealFloat a) =&gt; a -&gt; a -&gt; String
186+
densityTell mass volume
187+
| mass / volume &lt; 1.2 = "Wow! You're going for a ride in the sky!"
188+
| mass / volume &lt;= 1000.0 = "Have fun swimming, but watch out for sharks!"
189+
| otherwise = "If it's sink or swim, you're going to sink."
190190
</pre>
191-
<p>Let's see if I'm fat ...</p>
191+
<p>Let's see if cat food will float ...</p>
192192
<pre name="code" class="haskell:ghci">
193-
ghci&gt; bmiTell 85 1.90
194-
"You're supposedly normal. Pffft, I bet you're ugly!"
193+
ghci&gt; densityTell 400 1
194+
"Have fun swimming, but watch out for sharks!"
195195
</pre>
196-
<p>Yay! I'm not fat! But Haskell just called me ugly. Whatever!</p>
196+
<p>Looks like it will! At least until it dissolves into the pool... Yuck!</p>
197197
<p>Note that there's no <span class="fixed">=</span> right after the function name and its parameters, before the first guard. Many newbies get syntax errors because they sometimes put it there.</p>
198198
<p>Another very simple example: let's implement our own <span class="fixed">max</span> function. If you remember, it takes two things that can be compared and returns the larger of them.</p>
199199
<pre name="code" class="haskell:hs">

0 commit comments

Comments
 (0)