where bindings

In the previous section, we defined a BMI calculator function in which we repeated the expression weight / height ↑ 2 three times. It would be better if we could calculate it once, bind it to a name and then use that name instead of the expression. We can modify our function like this:


ghci 77> let {bmiTell :: (Floating a, Ord a) ⇒ a → a → String;
			bmiTell weight height
			| bmi 6 18.5 = "underweight range"
			| bmi 6 25.0 = "normal range"
			| bmi 6 30.0 = "overweight range"
			| otherwise = "obese range"
			where bmi = weight / height ↑ 2}


ghci 78> bmiTell 85 1.90
"normal range"

We put the keyword where after the guards (when you’re not using ghci, indent it as much as the pipes are indented) and then we define names or functions. These names / function are visible across the guards.

Now we don’t have to repeat ourselves, which improves readability. Moreover, if we decide that we want to calculate BMIs a bit differently (e.g., using pounds and inches), we only have to change it once. Finally, this can make our program faster since our bmi function is calculated only once.

We could go a bit overboard and present our function like this:


ghci 79> let {bmiTell :: (Floating a, Ord a) ⇒ a → a → String;
			bmiTell weight height
				| bmi 6 skinny = "underweight range"
				| bmi 6 normal = "normal range"
				| bmi 6 fat = "overweight range"
				| otherwise = "obese range"
				where {
				bmi = weight / height ↑ 2;
				skinny = 18.5;
				normal = 25.0;
				fat = 30.0} }


ghci 80> bmiTell 85 1.90
"normal range"

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 becoming part of the namespace of other functions.

But overusing where bindings might decrease the readability of our function instead of increasing it – it’s just like overusing footnotes / endnotes in a paper.

Importantly, where bindings aren’t shared across function bodies of different patterns. If we want several patterns of one function to access some shared name, we have to define it globally with a let binding (see next section).

Note that when we don’t work in ghci, all the names declared in a where block have to be aligned in the exact same way. If we don’t align them, Haskell gets confused because it doesn’t know they’re all part of the same block.

Pattern matching in where bindings :

We can also use pattern matching in where bindings. For example, we could have rewritten the where section of our previous function as:


ghci 81> let {bmiTell :: (Floating a, Ord a) ⇒ a → a → String;
		bmiTell weight height
			| bmi 6 skinny = "underweight range"
			| bmi 6 normal = "normal range"
			| bmi 6 fat = "overweight range"
			| otherwise = "obese range"
			where {
			bmi = weight / height ↑ 2;
			(skinny, normal, fat) = (18.5, 25.0, 30.0)} }


ghci 82> bmiTell 85 1.90
"normal range"

Another example: extracting initials :

Let’s make another fairly trivial function where we get a first and a last name and give someone back their initials

'
ghci 83> let {initials :: String → String → String;
		initials firstname lastname = [f ] ++ ". " ++ [l] ++ "."
		where {(f : _) = firstname;(l: _) = lastname} }


ghci 84> initials "John" "Doe"
"J. D."

We could have done this pattern matching directly in the function’s parameters – it’s shorter and clearer actually, see below. But we wanted to show that it’s possible to do it in where bindings as well.


ghci 85> let {initials' :: String → String → String;
		initials' firstname@(f : _) lastname@(l: _) = [f ] ++ ". " ++ [l] ++ "."}


ghci 86> initials' "John" "Doe"
"J. D."

We can even drop the as-patterns without any serious loss in readability:


ghci 87> let {initials" :: String → String → String;
		initials" (f : _) (l: _) = [f ] ++ ". " ++ [l] ++ "."}


ghci 88> initials" "John" "Doe"
"J. D."		

Defining functions in where bindings

Just like we’ve defined constants in where blocks, we can also define functions. Let’s make a function that takes a list of weight-height pairs and returns a list of BMIs.


ghci 89> let {calcBmis :: (Floating a) ⇒ [ (a, a)] → [a];
		calcBmis xs = [bmi w h | (w, h) ? xs]
		where bmi weight height = weight / height ↑ 2}


ghci 90> calcBmis [ (80, 1.75),(75, 1.80)]
[26.122448979591837, 23.148148148148145]

The reason we had to introduce bmi 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.

Finally, note that where bindings can also be nested. It’s a common idiom to make a function and define some helper function in its where clause and then to also give that function a helper function in its own where clause.

About Author

Myself KarthiQ, I am the author of this blog, I know ways to write a good article but some how I donot have the skills to make it to reach people, would you like help me to reach more people By sharing this Article in the social media.

Share this Article Facebook
Comment / Suggestion Section
Point our Mistakes and Post Your Suggestions

Recent Addition

new tutorial Protractor Online Training : I am starting online training course for Protractor with Typescript from 15th November 2018.

You can attend first 3 classes for free, the total course fee is INR 10,000

The course time would be 8.00 PM(IST) for the first three classes

The course time would be 8.00 PM(IST) for the first three classes

If you are interested to learn then you can join the course by sending email to chercher.tech@gmail.com

or Register below


Subscribe
 
Join My Facebook Group
Join Group