Haskell/Control structures

Haskell offers several ways of expressing a choice between different values. We explored some of them in the Haskell Basics chapters. This section will bring together what we have seen thus far, discuss some finer points, and introduce a new control structure.

if and guards revisited
We have already met these constructs. The syntax for  expressions is:

  is an expression which evaluates to a boolean. If the   is True then the  is returned, otherwise the  is returned. Note that in Haskell if is an expression (which is converted to a value) and not a statement (which is executed) as in many imperative languages. As a consequence, the <tt>else</tt> is mandatory in Haskell. Since <tt>if</tt> is an expression, it must evaluate to a result whether the condition is true or false, and the <tt>else</tt> ensures this. Furthermore, <tt><true-value></tt> and <tt><false-value></tt> must evaluate to the same type, which will be the type of the whole if expression. When  expressions are split across multiple lines, they are usually indented by aligning  s with  s, rather than with  s. A common style looks like this:

Guards and top-level  expressions are mostly interchangeable. With guards, the example above is a little neater:

Remember that  is just an alias to , and thus the last guard is a catch-all, playing the role of the final   of the   expression.

Guards are evaluated in the order they appear. Consider a set up like the following:

Here, the argument of  will be pattern-matched against pattern1. If it succeeds, then we proceed to the first set of guards: if predicate1 evaluates to, then   is returned. If not, then predicate2 is evaluated; and if it is true  is returned. Again, if not, then we proceed to the next case and try to match the argument against pattern2, repeating the guards procedure with predicate3 and predicate4. (Of course, if neither pattern matches or neither predicate is true for the matching pattern there will be a runtime error. Regardless of the chosen control structure, it is important to ensure all cases are covered.)

Embedding <tt>if</tt> expressions
A handy consequence of  constructs being expressions is that they can be placed anywhere a Haskell expression could be, allowing us to write code like this:

Note that we wrote the  expression without line breaks for maximum terseness. Unlike  expressions, guard blocks are not expressions; and so a   or a   definition is the closest we can get to this style when using them. Needless to say, more complicated one-line  expressions would be hard to read, making   and   attractive options in such cases.

Short-circuit operators
The  and   operators mentioned before are in fact control structures: they evaluate the first argument and then the second argument only if needed.

Avoiding excessive effort
For instance, suppose a large number n is to be checked to determine if it is a prime number and a function isPrime is available, but alas, it requires a lot of computation to evaluate. Using the function  will help if there are to be many evaluations with even values of n.

Avoidance of error conditions
can be used to avoid signalling a run-time error, such as divide-by-zero or index-out-of-bounds, etc. For instance, the following locates the last non-zero element of a list: Should all elements of the list be zero, the loop will work down to, and in this case the condition in the first guard will be evaluated without attempting to dereference element -1, which does not exist.

<tt>case</tt> expressions
One control structure we haven't talked about yet is  expressions. They are to piece-wise function definitions what  expressions are to guards. Take this simple piece-wise definition:

It is equivalent to - and, indeed, syntactic sugar for:

Whatever definition we pick, the same happens when  is called: The argument   is matched against all of the patterns in order, and on the first match the expression on the right-hand side of the corresponding equal sign (in the piece-wise version) or arrow (in the   version) is evaluated. Note that in this  expression there is no need to write   in the pattern; the wildcard pattern   gives the same effect.

Indentation is important when using. The cases must be indented further to the right than the beginning of the line containing the  keyword, and all cases must have the same indentation. For the sake of illustration, here are two other valid layouts for a  expression:

Since the left hand side of any case branch is just a pattern, it can also be used for binding, exactly like in piece-wise function definitions:

This function describes some properties of <tt>str</tt> using a human-readable string. Using case syntax to bind variables to the head and tail of our list is convenient here, but you could also do this with an if-expression (with a condition of  to pick the empty string case).

Finally, just like  expressions (and unlike piece-wise definitions),   expressions can be embedded anywhere another expression would fit:

The case block above fits in as any string would. Writing  this way makes  /  unnecessary (although the resulting definition is not as readable).

Controlling actions, revisited
In the final part of this chapter, we will introduce a few extra points about control structures while revisiting the discussions in the "Simple input and output" chapter. There, in the Controlling actions section, we used the following function to show how to execute actions conditionally within a  block using   expressions:

We can write the same  function using a <tt>case</tt> expression. To do this, we first introduce the Prelude function which takes two values of the same type (in the class) and returns a value of type   — namely one of,  ,  , depending on whether the first is greater than, less than, or equal to the second.

The <tt>do</tt>s after the s are necessary on the first two options, because we are sequencing actions within each case.

A note about
Now, we are going to dispel a possible source of confusion. In a typical imperative language (C, for example) an implementation of  might look like the following (if you don't know C, don't worry with the details, just follow the if-else chain):

This  first tests the equality case, which does not lead to a new call of , and the   has no accompanying . If the guess was right, a  statement is used to exit the function at once, skipping the other cases. Now, going back to Haskell, action sequencing in  blocks looks a lot like imperative code, and furthermore there actually is a  in Prelude. Then, knowing that expressions (unlike  expressions) do not force us to cover all cases, one might be tempted to write a literal translation of the C code above (try running it if you are curious)...

... but it won't work! If you guess correctly, the function will first print "You win!," but it will not exit at the. Instead, the program will continue to the  expression and check whether is less than. Of course it is not, so the else branch is taken, and it will print "Too high!" and then ask you to guess again. Things aren't any better with an incorrect guess: it will try to evaluate the case expression and get either  or  as the result of the. In either case, it won't have a pattern that matches, and the program will fail immediately with an exception (as usual, the incomplete  alone should be enough to raise suspicion).

The problem here is that  is not at all equivalent to the C (or Java etc.) statement with the same name. For our immediate purposes, we can say that  is a function. The  in particular evaluates to an action which does nothing. does not affect the control flow at all. In the correct guess case, the case expression evaluates to, an action of type , and execution just follows along normally.

The bottom line is that while actions and  blocks resemble imperative code, they must be dealt with on their own terms - Haskell terms.