© Stefania Loredana Nita and Marius Mihailescu 2019
Stefania Loredana Nita and Marius MihailescuHaskell Quick Syntax Referencehttps://doi.org/10.1007/978-1-4842-4507-1_11

11. Pattern Matching

Stefania Loredana Nita1  and Marius Mihailescu1
(1)
Bucharest, Romania
 

In Chapter 7, you learned the basics of pattern matching, which is used with functions. In this chapter, you’ll learn more about the details of pattern matching.

Basically, pattern matching means matching values with patterns and binding variables with the matches that succeeded.

Let’s take a closer look at the map function . The type signature and the definition are as follows:
map _ []     = []
map f (x:xs) = f x : map f xs
Here, you can identify four types of patterns .
  • f: This pattern matches basically anything and binds the f to anything that fits in.

  • (x:xs): This pattern matches a list with at least one element; the list contains something (which is bounded to x) created (with (:) operator) from something else (bounded to xs).

  • []: This pattern matches an empty list and does not bind anything.

  • _: This pattern matches everything but does not bind anything.

In the expression (x:xs), the x and xs can be considered subpatterns that match parts of a list, matching with everything that respects the types in the type signature of map. In other words, x matches with anything of type a, and xs matches with anything of type [a], in particular with an empty list. Therefore, a list with one element matches (x:xs).

Pattern matching is useful in the following situations :
  • Recognizing values: For example, map’s definition says that when an empty list is the second parameter, the result will be an empty list. In other words, it is chosen as the first branch.

  • Binding variables to identified values: For example, f, x, and xs from the previous definition are mapped to the arguments of map when called, and the second branch is chosen. Binding can be seen as a side effect of the fact that variables names are used as patterns, as using _ and [] suggests.

  • Sectioning values into more parts: For example, the (x:xs) expression binds x with the head of a list and xs with the tail of a list.

Further, let’s consider a function that duplicates the first element in a list. It should look like this:
g (y:ys) = y:y:ys
Observe that y:ys appears both on the left side and on the right side. To make the code easy to follow, you can use the as-pattern (@), which allows you to write y:ys just once.
g s@(y:ys) = y:s

Even the subpattern can fail, i.e., y:ys. The as-pattern always matches. Another advantage is that it can be faster because in the first version, the y:ys is reconstructed, instead of reusing the value to which it matched.

The result of a pattern matching process can have one of the following states:
  • Success: When a pattern matching process succeeds, the variables are bounded with the arguments.

  • Fail: When in a pattern matching process an equation fails, then the matching process moves to the next equation, and so on. If all equations fail, then a runtime error occurs.

  • Divergence: When a pattern matching process diverges , it means that a value needed in the pattern contains an error.

Pattern Matching and Constructors

Not all functions are allowed to be used in pattern matching. In pattern matching you use just constructors —those functions that construct data types.

Let’s examine the following piece of code:
data MyData = Zero | Double Int
g :: MyData -> Int
g Zero = 0
g (Double x) = 2*x

Here, Zero and Double are constructors for the MyData type. You can use them to pattern match Zero with the 0 value of the Int type and bind a value constructed with Double from MyData with its Int double.

Pattern matching works on lists, because on a naïve implementation you can interpret lists as defined with the data keyword.
data [a] = [] | a : [a]

Here, the type constructors for the list are the empty list and the (:) operator.

Note that the previous definition is not actually correct; we used it as an intuitive explanation of the reason why the pattern matching works on lists. In fact, in Haskell, lists are important, and they have a much more complex construction.

Tuples have a similar explanation.

Pattern matching is useful in records. For a comprehensive explanation of records, refer to Chapter 4.

Uses of Pattern Matching

You can use pattern matching in the following scenarios :
  • Equations (see the map example)

  • let expressions

    Prelude> y = let (x:_) = map (+8) [5,6] in x * 3
    Prelude> y
    39
  • where clauses

    Prelude> y = x * 3 where (x:_) = map (+8) [5,6]
    Prelude> y
    39
  • Lambda abstractions

    Prelude> switch = (a,b) -> (b,a)
    Prelude> switch (5,6)
    (6,5)
  • List comprehension

    associate :: Eq a => a -> [(a, t)] -> [t]
    associate character xs = [y | (x,y) <- xs , x == character]
    Prelude> associate 'D' [('D', 15), ('B', 0), ('D', 35), ('D', 100)]
    [15,35,100]
  • do blocks

    firstLetter = do
      (x:_) <- getLine
      putStrLn [x]

Summary

In this chapter, you learned the following:
  • What pattern matching is

  • How pattern matching and constructors are related

  • When to use pattern matching

References

  1. 1.

    M. Lipovaca, Learn You a Haskell for Great Good! A Beginner’s Guide (No Starch Press, 2011)

     
  2. 2.

    B. O’Sullivan, J. Goerzen, and D. B. Stewart, Real-World Haskell: Code You Can Believe In (O’Reilly Media, 2008)

     
  3. 3.

    T. Sheard and S. P. Jones, “Template Meta-programming for Haskell” in proceedings of the 2002 ACM SIGPLAN workshop on Haskell (ACM, 2002)

     
  4. 4.
     
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.105.190