Six ways to match patterns in Haskell

Sunday, Jan 31, 2021
Functional Haskell

Pattern matching in Haskell involves three basic patterns and three compound patterns. Every function definition can be seen as some combination of these six patterns.

Basic Patterns

Match exact value

This is the simplest form with pattern as a value that is matched against the argument directly. For example, to write an || function, we can match the direct boolean values enumerating all four cases explicitly:

(||) :: Bool -> Bool -> Bool
False || False = False
False || True = True
True || False = True
True || True = True

Semantically, the patterns are matched from top to bottom, and whichever pattern matches first, the corresponding expression is evaluated and becomes the result of the function.

Match any value

The wildcard pattern to match any value is accomplished by using _. For example, to write the above function more concisely, we can rewrite using wildcard pattern as:

(||) :: Bool -> Bool -> Bool
False || False = False
_ || _ = True

That is, the result is false only if both arguments are False. For all other cases (_), the result is True.

Variable binding

This is like the wildcard pattern in the sense that any value is matched. In addition however, the matched value gets bound to the variable used in the pattern. This variable can be used in the corresponding expression. For example, the above function can also be defined as follows:

(||) :: Bool -> Bool -> Bool
False || b = b
True || _ = True

In the first pattern, we bind the second argument to a variable b. This value is used in the expression. So we say that if the first argument is False, the second argument is bound to a variable, which is the result of that pattern. In the second pattern, we use the exact value pattern (True) and wildcard. We evaluate this pattern to True.

Compound Patterns

Compound patterns are used to match tuples or lists. Here, we still make use of basic patterns, but inside lists and tuples.

Tuple pattern

Tuple patterns are matched against tuples of same arity and corresponding patterns are matched against tuple components in same order. For example, (p1,p2,p3) will match any tuple with three components. First component is matched against p1, second against p2 and third against p3. In the example below, we use a tuple pattern to create a list containing second component from a list of pairs:

seconds :: [(a,b)] -> [b]
seconds ps = [y | (_,y) <- ps]

Note that this tuple pattern contains two basic patterns - wildcard and variable binding.

List pattern

List pattern is used to match lists of finite length. For example, [p1,p2] will match any list of length 2. First element is matched against p1 and second against p2. In the following example, we match any three character string:

threeChars :: String -> Bool
threeChars [_,_,_] = True
threeChars _ = False

Note that this list contain one basic pattern for all three patterns - wildcard.

Cons pattern

Cons pattern is used to match lists of any length. For example, to match a string of any length that begins with ‘a’, we can define the following function:

startsWith_a :: String -> Bool
startsWith_a ('a':_) = True
startsWith_a _ = False

Note that the cons pattern is enclosed inside parenthesis. In the first pattern, we match the head with ‘a’ and throw the tail away using wildcard. In the second pattern, we use wildcard to match every other string and evaluate that to False.