Haskell provides a notation for defining functions based on predicate values.
fx |predicate1=expression1 |predicate2=expression2 |predicate3=expression3
For instance, the absolute value of a number is its magnitude, i.e. ignoring its sign. You could define a function to calculate the absolute value with an if/then/else conditional
Notice how there is no equals sign on the first line of the function definition — but there is an equals sign after each guard.
The otherwise guard should always be last, it’s like the default case in a C-style switch statement.
Guards are easier to read than if/then/else if there are more than two conditional outcomes
For instance, think about scoring in the sport of Golf. For a single hole, a player takes a number of strokes. There is a ‘par’ score for the hole, which is the expected number of strokes.
holeScore::Int->Int->String holeScorestrokespar |strokes<par=show(par-strokes)++" under par" |strokes==par="level par" |strokes>par=show(strokes-par)++" over par"
How could we tidy this up? Maybe we could turn the final guard into otherwise and also refactor with a where clause.
holeScore::Int->Int->String holeScorestrokespar |score<0=show(absscore)++" under par" |score==0="level par" |otherwise=show(score)++" over par" wherescore=strokes-par
Notice that the score variable defined in the where clause is in scope for all three guards.
Case expressions
Recall from last week how we defined algebraic data types like binary trees. A value with an algebraic data type may have one of several different forms — such as a Leaf or a Node, in the case of Tree structures. Therefore to process such a value we need several segments of code, one for each possible form. The case expression examines the value, and chooses the corresponding clause. It’s like a guard, but it selects based on the form of the value, i.e. it does pattern matching.
Note that each pattern is followed by an arrow and then a value. Also note that each pattern is vertically aligned. Indentation really matters in Haskell!
OK, now suppose we want to make the data type a bit more sophisticated . Let’s add a Parrot with a String name.
Now the pattern includes a variable, which is associated with the concrete value for the Parrot’s name.
hello (Parrot "polly")
will return
"pretty polly"
In the same way as there is a catch-all case for guards (otherwise), we can have a catch-all pattern for a case. It’s the underscore character, _ which means ‘don’t care’ or ‘match anything’
How would you rewrite an if expression as a case expression? In fact, this is what happens in the core language. The if expression is just syntactic sugar that is rewritten automatically. Can you think of any more examples of syntactic sugar in Haskell? Please add your thoughts in the comments section below.