Six ways to match patterns in 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.