Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In which sense guards are better than imperative-if? (new to haskell )

Tags:

haskell

For the third time in my life I'm trying to learn Haskell, this time through Learn you a Haskell....
When the author explains guards, he shows this example:

bmiTell :: (RealFloat a) => a -> String 
bmiTell bmi  
| bmi <= 18.5 = "You're underweight, you emo, you!"  
| bmi <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"  
| bmi <= 30.0 = "You're fat! Lose some weight, fatty!"  
| otherwise   = "You're a whale, congratulations!"

and says

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.

I can see guards are more readable, but I don't get why that syntax is "far better"
It is more flexible? It is more powerful? What is the big advantage of guards?

My big issue it is probably the sentence

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

Can anyone give an example of that?

like image 945
Pablo Grisafi Avatar asked Oct 09 '12 13:10

Pablo Grisafi


1 Answers

Don gives the primary motivations for using guards, but in addition to that they also combine nicely with pattern matching. If all guards on a pattern fail it drops through to the next pattern, so you can check patterns and conditions simultaneously without having lots of duplicate fall-through cases. Here's a (very artificial) example:

expandRange x (Just lo, Just hi) | hi < lo = (Just x, Just x)
expandRange x (Just lo, hi) | x < lo = (Just x, hi)
expandRange x (lo, Just hi) | x > hi = (lo, Just x)
expandRange _ range = range

If we think of Nothing as being unbounded, this takes an element to compare and either "expands" a negative range to only that element, moves a lower/upper bound to include the element, or leaves the range unchanged if the element is already included.

Now, consider how you'd write the above without using guards! How many times would you end up duplicating a branch that's conceptually the same because the patterns differed? And yes, I realize this small example could be rewritten to avoid the issue entirely, but that's not always possible (or desirable).

This style of definition is, to my mind, the most significant thing you can express using guards that, while still possible, would be horrendously more verbose and much harder to read if it were written as a mixture of (unguarded) pattern cases and if expressions.

like image 156
C. A. McCann Avatar answered Feb 15 '23 18:02

C. A. McCann