Haskell/Control structures
From Wikibooks, the open-content textbooks collection
Haskell offers several ways of expressing a choice between different values. This section will describe them all and explain what they are for:
[edit] if Expressions
You have already seen these. The full syntax is:
if <condition> then <true-value> else <false-value>
else is required!If the <condition> is True then the <true-value> is returned, otherwise the <false-value> is returned. Note that in Haskell if is an expression (returning a value) rather than a statement (to be executed). Because of this the usual indentation is different from imperative languages. If you need to break an if expression across multiple lines then you should indent it like one of these:
if <condition> then <true-value> else <false-value>
if <condition>
then
<true-value>
else
<false-value>
Here is a simple example:
message42 :: Integer -> String
message42 n =
if n == 42
then "The Answer is forty two."
else "The Answer is not forty two."
Unlike many other languages, in Haskell the else is required. Since if is an expression, it must return a result, and the else ensures this.
[edit] case Expressions
case expressions are a generalization of if expressions. As an example, let's clone if as a case:
case <condition> of
True -> <true-value>
False -> <false-value>
_ -> error "Neither True nor False? File Not Found!"
First, this checks <condition> for a pattern match against True. If they match, the whole expression will evaluate to <true-value>, otherwise it will continue down the list. You can use _ as the pattern wildcard. In fact, the left hand side of any case branch is just a pattern, so it can also be used for binding:
case str of (x:xs) -> "The first character is " ++ [x] ++ "; the rest of the string is " ++ xs "" -> "This is the empty string."
This expression tells you whether str is the empty string or something else. Of course, you could just do this with an if-statement (with a condition of null str), but using a case binds variables to the head and tail of our list, which is convenient in this instance.
[edit] Equations and Case Expressions
You can use multiple equations as an alternative to case expressions. The case expression above could be named describeString and written like this:
describeString :: String -> String describeString (x:xs) = "The first character is " ++ [x] ++ "; the rest of the string is " ++ xs describeString "" = "This is the empty string."
Named functions and case expressions at the top level are completely interchangeable. In fact the function definition form shown here is just syntactic sugar for a case expression.
The handy thing about case expressions is that they can go inside other expressions, or be used in an anonymous function. TODO: this isn't really limited to case. For example, this case expression returns a string which is then concatenated with two other strings to create the result:
data Colour = Black | White | RGB Int Int Int
describeColour c =
"This colour is "
++ (case c of
Black -> "black"
White -> "white"
RGB _ _ _ -> "freaky, man, sort of in between")
++ ", yeah?"
You can also put where clauses in a case expression, just as you can in functions:
describeColour c =
"This colour is "
++ (case c of
Black -> "black"
White -> "white"
RGB red green blue -> "freaky, man, sort of " ++ show av
where av = (red + green + blue) `div` 3
)
++ ", yeah?"
[edit] Guards
As shown, if we have a top-level case expression, we can just give multiple equations for the function instead, which is normally neater. Is there an analogue for if expressions? It turns out there is.
We use some additonal syntax known as "guards". A guard is a boolean condition, like this:
describeLetter :: Char -> String describeLetter c | c >= 'a' && c <= 'z' = "Lower case" | c >= 'A' && c <= 'Z' = "Upper case" | otherwise = "Not a letter"
Note the lack of an = before the first |. Guards are evaluated in the order they appear. That is, if you have a set up similar to the following:
f (pattern1) | predicate1 = w
| predicate2 = x
f (pattern2) | predicate3 = y
| predicate4 = z
Then the input to f will be pattern-matched against pattern1. If it succeeds, then predicate1 will be evaluated. If this is true, then w is returned. If not, then predicate2 is evaluated. If this is true, then x is returned. Again, if not, then we jump out of this 'branch' of f and try to pattern match against pattern2, repeating the guards procedure with predicate3 and predicate4. If no guards match, an error will be produced at runtime, so it's always a good idea to leave an 'otherwise' guard in there to handle the "But this can't happen!" case.
The otherwise you saw above is actually just a normal value defined in the Standard Prelude as:
otherwise :: Bool otherwise = True
This works because of the sequential evaluation described a couple of paragraphs back: if none of the guards previous to your 'otherwise' one are true, then your otherwise will definitely be true and so whatever is on the right-hand side gets returned. It's just nice for readability's sake.
[edit] 'where' and guards
One nicety about guards is that where clauses are common to all guards.
doStuff x | x < 3 = report "less than three" | otherwise = report "normal" where report y = "the input is " ++ y
[edit] The difference between if and case
It's worth noting that there is a fundamental difference between if-expressions and case-expressions. if-expressions, and guards, only check to see if a boolean expression evaluated to True. case-expressions, and multiple equations for the same function, pattern match against the input. Make sure you understand this important distinction.