Skip to content

Commit f5765b9

Browse files
authored
rewrite a bunch more BMI examples to density (learnyouahaskell#27)
1 parent a03511e commit f5765b9

File tree

1 file changed

+38
-42
lines changed

1 file changed

+38
-42
lines changed

docs/syntax-in-functions.html

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -222,45 +222,41 @@ <h1 style="margin-left:-3px">Syntax in Functions</h1>
222222
</pre>
223223
<div class="hintbox"><em>Note:</em> Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it's easier to read that way.</div>
224224
<a name="where"></a><h2>Where!?</h2>
225-
<p>In the previous section, we defined a BMI calculator function and berator like this:</p>
225+
<p>In the previous section, we defined a density calculator function and responder like this:</p>
226226
<pre name="code" class="haskell:hs">
227-
bmiTell :: (RealFloat a) =&gt; a -&gt; a -&gt; String
228-
bmiTell weight height
229-
| weight / height ^ 2 &lt;= 18.5 = "You're underweight, you emo, you!"
230-
| weight / height ^ 2 &lt;= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
231-
| weight / height ^ 2 &lt;= 30.0 = "You're fat! Lose some weight, fatty!"
232-
| otherwise = "You're a whale, congratulations!"
233-
</pre>
234-
<p>Notice that we repeat ourselves here three times. We repeat ourselves three times. Repeating yourself (three times) while programming is about as desirable as getting kicked inna head. Since we repeat the same expression three times, it would be ideal if we could calculate it once, bind it to a name and then use that name instead of the expression. Well, we can modify our function like this:</p>
227+
densityTell :: (RealFloat a) =&gt; a -&gt; a -&gt; String
228+
densityTell mass volume
229+
| mass / volume &lt; 1.2 = "Wow! You're going for a ride in the sky!"
230+
| mass / volume &lt;= 1000.0 = "Have fun swimming, but watch out for sharks!"
231+
| otherwise = "If it's sink or swim, you're going to sink."
232+
</pre>
233+
<p>Notice that we repeat ourselves here two times. We repeat ourselves two times. Repeating yourself (two times) while programming is about as desirable as getting kicked inna head. Since we repeat the same expression twice, it would be ideal if we could calculate it once, bind it to a name and then use that name instead of the expression. Well, we can modify our function like this:</p>
235234
<pre name="code" class="haskell:hs">
236-
bmiTell :: (RealFloat a) =&gt; a -&gt; a -&gt; String
237-
bmiTell weight height
238-
| bmi &lt;= 18.5 = "You're underweight, you emo, you!"
239-
| bmi &lt;= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
240-
| bmi &lt;= 30.0 = "You're fat! Lose some weight, fatty!"
241-
| otherwise = "You're a whale, congratulations!"
242-
where bmi = weight / height ^ 2
243-
</pre>
244-
<p>We put the keyword <span class="fixed">where</span> after the guards (usually it's best to indent it as much as the pipes are indented) and then we define several names or functions. These names are visible across the guards and give us the advantage of not having to repeat ourselves. If we decide that we want to calculate BMI a bit differently, we only have to change it once. It also improves readability by giving names to things and can make our programs faster since stuff like our <span class="fixed">bmi</span> variable here is calculated only once. We could go a bit overboard and present our function like this:</p>
235+
densityTell :: (RealFloat a) =&gt; a -&gt; a -&gt; String
236+
densityTell mass volume
237+
| density &lt; 1.2 = "Wow! You're going for a ride in the sky!"
238+
| density &lt;= 1000.0 = "Have fun swimming, but watch out for sharks!"
239+
| otherwise = "If it's sink or swim, you're going to sink."
240+
where density = mass / volume
241+
</pre>
242+
<p>We put the keyword <span class="fixed">where</span> after the guards (usually it's best to indent it as much as the pipes are indented) and then we define several names or functions. These names are visible across the guards and give us the advantage of not having to repeat ourselves. If we decide that we want to calculate density a bit differently, we only have to change it once. It also improves readability by giving names to things and can make our programs faster since stuff like our <span class="fixed">density</span> variable here is calculated only once. We could go a bit overboard and present our function like this:</p>
245243
<pre name="code" class="haskell:hs">
246-
bmiTell :: (RealFloat a) =&gt; a -&gt; a -&gt; String
247-
bmiTell weight height
248-
| bmi &lt;= skinny = "You're underweight, you emo, you!"
249-
| bmi &lt;= normal = "You're supposedly normal. Pffft, I bet you're ugly!"
250-
| bmi &lt;= fat = "You're fat! Lose some weight, fatty!"
251-
| otherwise = "You're a whale, congratulations!"
252-
where bmi = weight / height ^ 2
253-
skinny = 18.5
254-
normal = 25.0
255-
fat = 30.0
244+
densityTell :: (RealFloat a) =&gt; a -&gt; a -&gt; String
245+
densityTell mass volume
246+
| density &lt; air = "Wow! You're going for a ride in the sky!"
247+
| density &lt;= water = "Have fun swimming, but watch out for sharks!"
248+
| otherwise = "If it's sink or swim, you're going to sink."
249+
where density = mass / volume
250+
air = 1.2
251+
water = 1000.0
256252
</pre>
257253
<p>The names we define in the where section of a function are only visible to that function, so we don't have to worry about them polluting the namespace of other functions. Notice that all the names are aligned at a single column. If we don't align them nice and proper, Haskell gets confused because then it doesn't know they're all part of the same block.</p>
258254
<p><i>where</i> bindings aren't shared across function bodies of different patterns. If you want several patterns of one function to access some shared name, you have to define it globally.</p>
259255
<p>You can also use where bindings to <em>pattern match</em>! We could have rewritten the where section of our previous function as:</p>
260256
<pre name="code" class="haskell:hs">
261257
...
262-
where bmi = weight / height ^ 2
263-
(skinny, normal, fat) = (18.5, 25.0, 30.0)
258+
where density = mass / volume
259+
(air, water) = (1.2, 1000.0)
264260
</pre>
265261
<p>Let's make another fairly trivial function where we get a first and a last name and give someone back their initials.</p>
266262
<pre name="code" class="haskell:hs">
@@ -270,13 +266,13 @@ <h1 style="margin-left:-3px">Syntax in Functions</h1>
270266
(l:_) = lastname
271267
</pre>
272268
<p>We could have done this pattern matching directly in the function's parameters (it would have been shorter and clearer actually) but this just goes to show that it's possible to do it in where bindings as well.</p>
273-
<p>Just like we've defined constants in where blocks, you can also define functions. Staying true to our healthy programming theme, let's make a function that takes a list of weight-height pairs and returns a list of BMIs.</p>
269+
<p>Just like we've defined constants in where blocks, you can also define functions. Staying true to our solids programming theme, let's make a function that takes a list of mass-volume pairs and returns a list of densities.</p>
274270
<pre name="code" class="haskell:hs">
275-
calcBmis :: (RealFloat a) =&gt; [(a, a)] -&gt; [a]
276-
calcBmis xs = [bmi w h | (w, h) &lt;- xs]
277-
where bmi weight height = weight / height ^ 2
271+
calcDensities :: (RealFloat a) =&gt; [(a, a)] -&gt; [a]
272+
calcDensities xs = [density m v | (m, v) &lt;- xs]
273+
where density mass volume = mass / volume
278274
</pre>
279-
<p>And that's all there is to it! The reason we had to introduce <span class="fixed">bmi</span> as a function in this example is because we can't just calculate one BMI from the function's parameters. We have to examine the list passed to the function and there's a different BMI for every pair in there.</p>
275+
<p>And that's all there is to it! The reason we had to introduce <span class="fixed">density</span> as a function in this example is because we can't just calculate one density from the function's parameters. We have to examine the list passed to the function and there's a different density for every pair in there.</p>
280276
<p><i>where</i> bindings can also be nested. It's a common idiom to make a function and define some helper function in its <i>where</i> clause and then to give those functions helper functions as well, each with its own <i>where</i> clause.</p>
281277
<a name="let-it-be"></a><h2>Let it be</h2>
282278
<p>
@@ -318,17 +314,17 @@ <h1 style="margin-left:-3px">Syntax in Functions</h1>
318314
ghci&gt; (let (a,b,c) = (1,2,3) in a+b+c) * 100
319315
600
320316
</pre>
321-
<p>You can also put <i>let</i> bindings inside list comprehensions. Let's rewrite our previous example of calculating lists of weight-height pairs to use a <i>let</i> inside a list comprehension instead of defining an auxiliary function with a <i>where</i>.</p>
317+
<p>You can also put <i>let</i> bindings inside list comprehensions. Let's rewrite our previous example of calculating lists of mass-volume pairs to use a <i>let</i> inside a list comprehension instead of defining an auxiliary function with a <i>where</i>.</p>
322318
<pre name="code" class="haskell:hs">
323-
calcBmis :: (RealFloat a) =&gt; [(a, a)] -&gt; [a]
324-
calcBmis xs = [bmi | (w, h) &lt;- xs, let bmi = w / h ^ 2]
319+
calcDensities :: (RealFloat a) =&gt; [(a, a)] -&gt; [a]
320+
calcDensities xs = [density | (m, v) &lt;- xs, let density = m / v]
325321
</pre>
326-
<p>We include a <i>let</i> inside a list comprehension much like we would a predicate, only it doesn't filter the list, it only binds to names. The names defined in a <i>let</i> inside a list comprehension are visible to the output function (the part before the <span class="fixed">|</span>) and all predicates and sections that come after of the binding. So we could make our function return only the BMIs of fat people:</p>
322+
<p>We include a <i>let</i> inside a list comprehension much like we would a predicate, only it doesn't filter the list, it only binds to names. The names defined in a <i>let</i> inside a list comprehension are visible to the output function (the part before the <span class="fixed">|</span>) and all predicates and sections that come after of the binding. So we could make our function return only the densities that will float in air:</p>
327323
<pre name="code" class="haskell:hs">
328-
calcBmis :: (RealFloat a) =&gt; [(a, a)] -&gt; [a]
329-
calcBmis xs = [bmi | (w, h) &lt;- xs, let bmi = w / h ^ 2, bmi &gt;= 25.0]
324+
calcDensities :: (RealFloat a) =&gt; [(a, a)] -&gt; [a]
325+
calcDensities xs = [density | (m, v) &lt;- xs, let density = m / v, density &lt; 1.2]
330326
</pre>
331-
<p>We can't use the <span class="fixed">bmi</span> name in the <span class="fixed">(w, h) &lt;- xs</span> part because it's defined prior to the <i>let</i> binding.</p>
327+
<p>We can't use the <span class="fixed">density</span> name in the <span class="fixed">(m, v) &lt;- xs</span> part because it's defined prior to the <i>let</i> binding.</p>
332328
<p>We omitted the <i>in</i> part of the <i>let</i> binding when we used them in list comprehensions because the visibility of the names is already predefined there. However, we could use a <i>let in</i> binding in a predicate and the names defined would only be visible to that predicate. The <i>in</i> part can also be omitted when defining functions and constants directly in GHCi. If we do that, then the names will be visible throughout the entire interactive session.</p>
333329
<pre name="code" class="haskell:ghci">
334330
ghci&gt; let zoot x y z = x * y + z

0 commit comments

Comments
 (0)