[Pattern matching and guards. Bryan O'Sullivan **20070720070235] { hunk ./en/ch03-funcs-types.xml 347 - + hunk ./en/ch03-funcs-types.xml 952 - - Strong and Static Typing With Inference - FIXME - See also . - + + Pattern matching hunk ./en/ch03-funcs-types.xml 955 - - Pattern Matching + Although we introduced a handful of functions earlier that + can operate on lists, we've yet to see how we might generally + get values out of a constructed algebraic data type. Haskell + has a simple pattern matching facility that we can use to this + end. hunk ./en/ch03-funcs-types.xml 961 - FIXME: Introduce pattern matching. Show how to write a - function as a series of clauses, each predicated on its - patterns. - - + A pattern lets us peer inside a compound value and bind + variables to the values it contains. In fact, when we define a + function, the parameters to that function are really patterns + that bind our variables to an entire value. hunk ./en/ch03-funcs-types.xml 966 - - Basic Types - FIXME: Type basics: products (tuples), sums - (Maybe,Either), recursive types (lists). Give us enough glue to - pattern match on. - - + Here's an example of pattern matching in action on a list; + we're going to add all elements of the list together. hunk ./en/ch03-funcs-types.xml 969 - - Guards - FIXME: Introduce guards. Show that guards and patterns - can be used together or independently. - - + &add.hs:sumList; + + See that (x:xs) on the left of the first line? + The : means match the head of a + list; that's the familiar list constructor, + (:), in action in a new way. The variables + x and xs are given the + values of (bound to) the head and tail of the + list, respectively. The whole pattern is wrapped in parentheses + so Haskell won't parse it as three separate arguments. + + What effect does pattern matching have? Haskell will only + evaluate the right hand side of an equation if it can match all + of the patterns on the left hand side. In the definition of + sumList above, the right hand side of the + first equation won't be evaluated if the input list is empty. + Instead, Haskell will fall through to the + equation on the following line, which does + have a pattern for the empty list, and it will evaluate + that. + + It might initially look like we have two functions named + sumList here, but Haskell lets us define a + function as a series of equations, so in fact these two clauses + are defining the behaviour of one function, for different + inputs. (By the way, there's a standard function, + sum, that does + + The syntax for pattern matching on a tuple is similar to the + syntax for constructing a tuple. Here's a function that returns + the third element from a three-tuple. + + &Tuple.hs:third; + + There's no limit on how deep within a value a + pattern can look. Here's a definition that looks both inside a + tuple and inside a list within that tuple. + + &Tuple.hs:complicated; + + We can try this out interactively. + + &tuple.ghci:complicated; + + Wherever a literal value is present in a pattern, that value + must match exactly for the pattern match to succeed. If every + pattern within a series of equations fails to match, we get a + runtime error. + + &tuple.ghci:nomatch; + + We can pattern match on algebraic data types using their + constructors. Remember the Wrapper type we defined + earlier? Here's how we can extract a wrapped value from a + Wrapper. + + &Wrapper.hs:unwrap; + + Let's see it in action. + + &wrapper.ghci:unwrap; + + Notice that Haskell infers the type of the + unwrap function based on the constructor + we're using in our pattern. If we're trying to match a value + whose constructor is Wrapper, then the type + of that parameter must be Wrapper a. + + + Haskell tests patterns for matches in the order in which + we list them in our code. It goes from top to bottom and + stops at the first match; it does not + check every pattern and use the best match. + + If you're familiar with pattern matching from a logic + programming language like Prolog, Haskell's facility is + simpler and less powerful. It doesn't provide backtracking or + unification. + + + + The don't-care, or wild card, pattern + + When we're writing a pattern, we can specify that we don't + care what value a particular value within a structure has, + without actually binding that value to a name. The notation + for this is _ (called a wild card or don't + care), and we use it as follows. This function + tells us whether the result of the roots + function we defined earlier is real-valued or not. + + &Roots.hs:isRealValued; + + Here, we don't care about the value of the result, just + about which constructor was used to create it. If it was + Left, the result must be a complex + number, otherwise it must be real. We can use a wild card for + the entire second pattern; there's no need to see if the + constructor is Right, because it + must be; Either only has two + constructors. + + + + The case expression hunk ./en/ch03-funcs-types.xml 1075 - - Conditionals: if and case - FIXME + We're not limited to using patterns in function + definitions. The case expression lets us match + patterns at any time. Here's what it looks like. + + &Roots.hs:hasRealRoots; + + The case keyword is followed by an arbitrary + expression; the result of this expression is what we're + pattern matching on. The of keyword signifies + the end of the expression and the beginning of the block of + patterns and expressions. + + Each item in the block consists of a pattern, followed by + an arrow ->, followed by an expression to + evaluate if that pattern matches. The result of the + case expression is the result of the expression + associated with the first pattern to match, taken from top to + bottom. + + To express here's the expression to evaluate if + none of the other patterns match, we would just use + the wild card pattern _ as the last in our list + of patterns. + + + + A flying visit back to the where clause + + Now that we've seen that we can define a function as a + series of equations, the usefulness of the where + clause should be a bit more clear. Variables that we define + inside a where clause are visible across all of + the equations that precede it in a single block. + + hunk ./en/ch03-funcs-types.xml 1112 - - Local definitions: let and where - FIXME + + Conditional evaluation with guards + + We can further extend our expressive arsenal using + guards. A guard is an expression of type + Bool; if it evaluates to True, + the equation that follows it is evaluated. Otherwise, the next + guard in the series is evaluated, and so on. Here's an example + of guards in action. + + &Roots.hs:guardedRoots; + + Each guard is introduced by a | symbol, + followed by the guard expression, then an = symbol + (or -> if within a case + expression), then the expression to evaluate if the guard + succeeds. + + We can use guards anywhere that we can use patterns. The + combination of writing a function as a series of equations, + pattern matching, and guards lets us write code that's + clear and easy to understand. + + Remember the myTake function we defined + in ? + + &myTake.hs:myTake.noid; + + Here's a reformulation of that function using patterns and + guards. Instead of reasoning about what an if + expression is doing and which branch will be evaluated, the code + uses a series of equations with simple patterns and guards. This + makes it easy to understand its behaviour under different + circumstances. + + &myTake.hs:niceTake; + hunk ./examples/ch03/Roots.hs 10 - where n = b**2 - 4 * a * c - a2 = 2 * a - n' = n :+ 0 - b' = b :+ 0 + where n = b**2 - 4 * a * c + a2 = 2 * a + n' = n :+ 0 + b' = b :+ 0 + a2' = a2 :+ 0 hunk ./examples/ch03/Roots.hs 17 +{-- snippet isRealValued --} +isRealValued :: Either (Complex Double, Complex Double) (Double, Double) + -> Bool +isRealValued (Left _) = False +isRealValued _ = True +{-- /snippet isRealValued --} + hunk ./examples/ch03/Roots.hs 36 +{-- snippet hasRealRoots --} +hasRealRoots a b c = case realRoots a b c of + Just _ -> True + Nothing -> False +{-- /snippet hasRealRoots --} + +{-- snippet guardedRoots --} +guardedRoots a b c + | n > 0 && a /= 0 = Just (r1, r2) + | otherwise = Nothing + where n = b**2 - 4 * a * c + a2 = 2 * a + r1 = (-b + sqrt n) / a2 + r2 = (-b - sqrt n) / a2 +{-- /snippet guardedRoots --} + addfile ./examples/ch03/Tuple.hs hunk ./examples/ch03/Tuple.hs 1 +{-- snippet third --} +third (a, b, c) = c +{-- /snippet third --} + +{-- snippet complicated --} +complicated (True, a, x:xs, 5) = (a, xs) +{-- /snippet complicated --} hunk ./examples/ch03/Wrapper.hs 20 +{-- snippet unwrap --} +unwrap (Wrapper x) = x +{-- /snippet unwrap --} + hunk ./examples/ch03/add.hs 5 +{-- snippet sumList --} +sumList (x:xs) = x + sumList xs +sumList [] = 0 +{-- /snippet sumList --} + hunk ./examples/ch03/myTake.hs 13 +{-- snippet niceTake --} +niceTake n _ | n <= 0 = [] +niceTake _ [] = [] +niceTake n (_:xs) = niceTake (n - 1) xs +{-- /snippet niceTake --} + hunk ./examples/ch03/tuple.ghci 4 + hunk ./examples/ch03/tuple.ghci 8 +--# complicated +:load Tuple.hs +complicated (True, 1, [1,2,3], 5) + +--# nomatch +complicated (False, 1, [1,2,3], 5) + hunk ./examples/ch03/wrapper.ghci 11 +--# unwrap +unwrap (Wrapper "foo") +:type unwrap + }