Skip to content

Pattern Guards

The version of our sign function in the previous section is a marked improvement over the previous versions in terms of readability, but it's still not how a seasoned Haskell programmer would write it. Here is probably how they would do it:

sign :: Int -> Int
sign 0             =  0
sign x | x > 0     =  1
       | otherwise = -1

This definition uses two features. The first one is pattern matching, which we discussed in the previous section. The second one is pattern guards. These are conditions that we can attach to patterns. The important thing is that these conditions can be arbitrary Boolean expressions. If a pattern matches but its attached condition is not satisfied, then we are not allowed to use this branch. The syntax for pattern guards is simple. You separate the pattern from its attached condition using a vertical bar. You can have multiple sub-branches corresponding to different conditions. For these sub-branches, the vertical bars need to line up. If lines get too long, I tend to put the pattern guards on new lines from the pattern, but in this case the vertical bars still need to line up, like so:

sign :: Int -> Int
sign 0 = 0
sign x
    | x > 0     =  1
    | otherwise = -1

So how do pattern guards work? Let's work through our sign function. If we call sign with an argument that is 0, then the first equation sign 0 = 0 applies because our argument matches the pattern 0, and this pattern has no pattern guards (conditions) attached to it. So we return 0 as before. If the argument is not 0, then the first equation does not apply, and we check the second equation. The pattern x of this equation matches any argument, and we end up binding x to this argument. However, this equation has two sub-branches, each with a pattern guard. So we check the condition attached to the first branch, x > 0. If this condition is satisfied, then we take this branch and we return 1. If this condition is not satisfied, then we are not allowed to take the first sub-branch, and we go on to checking the condition of the second sub-branch. Here, the condition of the second sub-branch is otherwise, which is a condition that always succeeds.1 Thus, we end up taking the second sub-branch and return -1, which is the right answer because we know that the argument is neither 0 nor positive, so it must be negative.

A few more comments are in order. First, we can have as many sub-branches as we want. Here, we only needed two, but there are some case distinctions that branch into many different cases depending on a number of conditions.

Second, it is not required that some sub-branch succeeds for every pattern. To illustrate this, we could have written our sign function as

sign :: Int -> Int
sign 0         =  0
sign x | x > 0 =  1
sign _         = -1

Let's parse this. Again, if the argument is 0, then the argument matches the pattern of the first equation and the result is 0. if the argument is non-zero, then the first equation cannot be used and we check the second equation. In this equation, the pattern x matches the argument and gets bound to the argument. However, we have the condition x > 0 attached to this pattern. If this condition is satisfied, we are allowed to use this equation and return 1, which is correct because our argument is positive. If the condition is not satisfied, then the entire second equation fails because it has only one sub-branch and the condition attached to this sub-branch is not satisfied. Thus, we try the third equation, sign _ = -1. In this case, we do not care anymore what the argument is because we know that it is negative, given that the first two equations failed. Thus, we use the wildcard _ for the argument in this equation and unconditionally return -1.

Just to be sure that this definition does indeed do the right thing, here is the result of playing with it in GHCi:

GHCi
>>> :{
  | sign 0         =  0
  | sign x | x > 0 =  1
  | sign _         = -1
  | :}
>>> sign 5
1
>>> sign (-10)
-1
>>> sign 0
0

The third comment is that we can attach pattern guards also to the branches of a case expression. This shouldn't be all that surprising, given that I mentioned in the previous section that a definition of a function with multiple equations is really a single equation with a case expression. The two definitions of sign above can be written using a case expression as

sign :: Int -> Int
sign x = case x of
    0             ->  0
    _ | x > 0     ->  1
      | otherwise -> -1

or

sign :: Int -> Int
sign x = case x of
    0         ->  0
    _ | x > 0 ->  1
    _         -> -1

To summarize, we can implement branching into various alternative paths of computation in Haskell using if then else expressions, case expressions, multiple equations and pattern matching, and pattern guards.

Exercise

Assume we want to implement a sign function that tells us in plain English whether a number is positive, negative or zero. If \(x = 0\), the function should produce the string "zero". If \(x > 0\), the string should be "positive". If \(x < 0\), the string should be "negative". Here's an attempt implementing such a function:

strSign :: Int -> String
strSign x = case x == 0 of
    True                  -> "zero"
    otherwise | x > 0     -> "positive"
              | otherwise -> "negative"
    _                     -> "Oops! Something went wrong!"

The logic seems impeccable. If x == 0, the result is "zero". Otherwise, if x > 0, the result is "positive". Otherwise, the result is "negative". In addition, we pride ourselves in being good software engineers who always catch unexpected behaviour and deal with it gracefully instead of simply letting our program crash, so we added a catch-all branch with a wildcard pattern. We don't expect this branch to ever be reached, so the string in this case is "Oops! Something went wrong!". Now let's try this out:

GHCi
>>> :{
  | strSign x = case x == 0 of
  |     True                  -> "zero"
  |     otherwise | x > 0     -> "positive"
  |               | otherwise -> "negative"
  |     _                     -> "Oops! Something went wrong!"
  | :}

<interactive>:5:17: warning: [-Woverlapping-patterns]
    Pattern match is redundant
    In a case alternative: otherwise | otherwise -> ...
>>> strSign 0
"zero"
>>> strSign 1
"positive"
>>> strSign (-1)
"Oops! Something went wrong!"

The warning that GHCi spits out, "Pattern match is redundant", tells us that the branch otherwise | otherwise is never used, and our attempt to evaluate strSign (-1) confirms this: The result is "Oops! Something went wrong!", not "negative", as we expected. Explain why the function misbehaves.

Hint: Read the footnote on this page.

Solution

As explained in the footnote, otherwise is simply a variable, not a keyword. Here, the first branch of the case expression applies if x == 0. Thus, the second branch is tried only when x == 0 evaluates to False. The pattern match for the second branch then sets otherwise = False. If x < 0, the pattern guard x > 0 of the first sub-branch fails. The pattern guard of the second sub-branch is otherwise. Since pattern matching sets otherwise = False, this pattern guard fails too! Since neither of the two sub-branches of the otherwise branch of the case expression succeeds, we try the final branch. This branch has a wildcard pattern and no pattern guards, so it always succeeds, and the result is "Oops! Something went wrong!" for any negative argument.

The footnote suggested that we could try to intentionally break perfectly fine Haskell code by redefining otherwise. We'd normally never do this. What happend here was an accidental redefinition of otherwise, by using it as a pattern. The morale of the story: Never use otherwise anywhere in your code except as the final pattern guard attached to some pattern.

Exercise

Implement a function myAbs that returns the absolute value of its argument. We discussed here how to implement such a function using an if then else expression. Use pattern guards instead in this exercise.

Solution
myAbs :: Int -> Int
myAbs x | x > 0     =  x
        | otherwise = -x

Bonus: The Haskell standard library provides a signum function for number types, which does exactly the same as the sign function we beat to death in this chapter. Can you figure out a way to implement myAbs completely without case distinctions?

Solution
myAbs :: Int -> Int
myAbs x = x * signum x

  1. Try

    GHCi
    >>> :info otherwise
    >>> otherwise
    

    Can you guess what otherwise is? It sounds like a keyword, such as else, but it's actually a plain old value, defined as otherwise = True. Since a pattern guard is a Boolean expression, and otherwise is a Boolean expression that is always true, this allows us to write pattern guards that say, "otherwise, if none of the previous branches matched, use this branch". What a sneaky little hack.

    Given that otherwise is a plain old value, you are free to redefine it in your code. Don't! If you want to drive a fellow Haskell programmer insane and gift them hours of debugging to figure out why the otherwise branch of a function definition doesn't run, you could define otherwise = False