Skip main navigation

Guards, Guards!

There are several elegant ways to define functions in Haskell. In this article, Dr Jeremy Singer explores guards and case expressions.
Photo of Iron Gate
© University of Glasgow

Haskell provides a notation for defining functions based on predicate values.

f x
| 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

absolute x = if (x<0) then (-x) else x

or with guards

absolute x
| x<0 = -x
| otherwise = x

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
holeScore strokes par
| 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
holeScore strokes par
| score < 0 = show (abs score) ++ " under par"
| score == 0 = "level par"
| otherwise = show(score) ++ " over par"
where score = 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.

Here is a sum data type for my pets.

data Pet = Cat | Dog | Fish

And here is how I greet my pets.

hello :: Pet -> String
hello x =
case x of
Cat -> "meeow"
Dog -> "woof"
Fish -> "bubble"

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.

data Pet = Cat | Dog | Fish | Parrot String

hello :: Pet -> String
hello x =
case x of
Cat -> "meeow"
Dog -> "woof"
Fish -> "bubble"
Parrot name -> "pretty " ++ 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’

So we could redefine hello as:

hello :: Pet -> String
hello x =
case x of
Parrot name -> "pretty " ++ name
_ -> "grunt"

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.

© University of Glasgow
This article is from the free online

Functional Programming in Haskell: Supercharge Your Coding

Created by
FutureLearn - Learning For Life

Reach your personal and professional goals

Unlock access to hundreds of expert online courses and degrees from top universities and educators to gain accredited qualifications and professional CV-building certificates.

Join over 18 million learners to launch, switch or build upon your career, all at your own pace, across a wide range of topic areas.

Start Learning now