[Even more rewriting Bryan O'Sullivan **20080119080055] { move ./examples/ch03/BogusPattern.hs ./examples/ch04/BogusPattern.hs addfile ./examples/ch04/BadTree.hs addfile ./examples/ch04/GoodTree.hs hunk ./en/ch04-defining-types.xml 375 - See that (x:xs) on the left of the first line? - The : means match the head of a + See that (x:xs) on the left of the + first line? The : means match the head of a hunk ./en/ch04-defining-types.xml 384 - 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. + 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. hunk ./en/ch04-defining-types.xml 402 - - Ordering is important + + Ordering is important hunk ./en/ch04-defining-types.xml 405 - When applying a function, a Haskell + When applying a function, a Haskell hunk ./en/ch04-defining-types.xml 409 - + hunk ./en/ch04-defining-types.xml 411 - As a final note, there already exists a standard function, + As a final note, there already exists a standard function, hunk ./en/ch04-defining-types.xml 436 - Let's consider what happens if we - match the pattern (x:xs) against our example - expression 'a': ['b']. + Let's consider what happens if we match the pattern + (x:xs) against our example expression 'a': + ['b']. hunk ./en/ch04-defining-types.xml 569 - result, just about which constructor was used to create it. - If the constructor was Just, the result - must be a real number, otherwise it's either infinite or - complex. We can use a wild card for the entire second - pattern; there's no need to see if the constructor is + result, just about which constructor was used to create it. If + the constructor was Just, the result must + be a real number, otherwise it's either infinite or complex. + We can use a wild card for the entire second pattern; there's + no need to see if the constructor is hunk ./en/ch04-defining-types.xml 578 - In a pattern, a wild card acts similarly - to a variable, only it doesn't bind the value to a name. - While we can't put the same variable name multiple times in a - single pattern, we can use a wild card as many times as we - need to. + In a pattern, a wild card acts similarly to a + variable, only it doesn't bind the value to a name. While we + can't put the same variable name multiple times in a single + pattern, we can use a wild card as many times as we need + to. hunk ./en/ch04-defining-types.xml 584 - Another advantage of wild cards is that a Haskell compiler - can warn us if we introduce a variable name in a pattern, but - don't use it in a function's body; defining something but - forgetting to use it can often indicate a bug. Using a wild - card instead of an unused variable makes it explicit that we - really don't care what value is present, and will prevent such - a warning. + Another advantage of wild cards is that a + Haskell compiler can warn us if we introduce a variable name + in a pattern, but don't use it in a function's body; defining + something but forgetting to use it can often indicate a bug. + Using a wild card instead of an unused variable makes it + explicit that we really don't care what value is present, and + will prevent such a warning. hunk ./en/ch04-defining-types.xml 592 - Wild cards also help readability, as they make it easier - to tell which values we're really using. + Wild cards also help readability, as they make + it easier to tell which values we're really using. hunk ./en/ch04-defining-types.xml 1081 - - The case expression - - 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: this is the expression that we're - checking0. 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. - - - - - Common beginner mistakes with patterns - - There are a few ways in which new Haskell - programmers can misunderstand or misuse patterns. Here are a - few potential missteps that you can easily avoid. - - There's no way to write a pattern that compares a - value with a variable. Matching a pattern only lets us perform - exact comparisons against combinations of constructors and - simple values. - - Here's a well-intentioned example of pattern - matching gone awry. This code compiles cleanly, but depending - on what you expect it to do, it might surprise you. - - &BogusPattern.hs:isHead; - - A naive glance suggests that this code is trying - to check the value of f to see if it's - actually the standard function head, but - here's what it is really doing. - - Because the first pattern in the case - expression is a variable, this branch of the case - will always match, no matter what the value - of f is. The name head - thus acts as a local variable whose value is the value of - f, which hides the global definition of the - well-known head function. - - - Irrefutable patterns - - A pattern that consists only of a variable will - always match, because it's not being compared against any - value that could cause the match to fail. We refer to - patterns that always match as - irrefutable. The wild card _ - is also irrefutable. - - - The first pattern always matches, because it's - irrefutable. But the second pattern also - always matches, because it uses a wild card. However, because - Haskell attempts to match patterns in the order in which we - write them, the first pattern will always succeed, and the - second pattern will never actually be reached. Because the two - patterns will match the same values, they are said to - overlap. If &GHC; ever complains to you - about overlapping patterns, it's telling you that one of your - patterns is the same as another, and so it will never actually - be matched. - - Another thing to be aware of is that a variable - can only appear once in a pattern. For example, we can't put a - variable in multiple places within a pattern to express the - notion this value and that should be - identical. - - The way around these restrictions of Haskell's - patterns is to use patterns in combination with a language - facility called guards, which we'll talk - about next. - - - - 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. A series of guards - is only checked if the patterns that they're associated with - match. 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. A guard expression can use any variables matched in - the pattern that precedes it. - - The otherwise used in the second guard - has an obvious meaning: it's the expression to evaluate if - previous guards all evaluate to False. It's - not a special piece of syntax, though; it's just a predefined variable - whose value is True. - - We can use guards anywhere that we can use - patterns. The advantage of writing a function as a series of - equations using pattern matching and guards is that it often - makes code much clearer. Remember the - myDrop function we defined in ? - - &myDrop.hs:myDrop.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. Hoisting the control decisions to the - outside of the code, instead of burying it inside - with if expressions, lets us enumerate up front - the cases in which we expect the behaviour of the function to - differ. - - &myDrop.hs:niceDrop; - - Let's return to one of the limitations of patterns that we - mentioned in the previous section: the fact that we can't - check two variables within a pattern for equality. - We can express this quite easily by following the pattern with a - guard. - - &Guard.hs:secondEqualsThird; - - Here, for good measure, we've illustrated guard - syntax in a case expression. This guard expression - compares the variables matched in the pattern for equality. - - - - Infix functions - - Usually, when we define or call a function in Haskell, we - write the name of the function, followed by its arguments; this - is called prefix notation, because the name of the function - comes before its arguments. For a function that takes two - arguments, we have the option of using it in - infix form, between its first and second - arguments. This allows us to write expressions using functions - as if they were infix operators. - - The syntax for defining or calling a function in infix form - is to enclose the name of the function in backtick characters - (sometimes known as backquotes). Here's a simple infix - definition. - - &Plus.hs:plus; - - Defining a function in infix form doesn't change anything - about the behaviour of the function. We can call the function - using infix or prefix notation, as we prefer. - - &infix.ghci:plus; - - Infix notation is useful for more than just our own - functions. For example, Haskell's standard - Data.List module defines a function, - isPrefixOf, that indicates whether all - elements of its first argument are equal to the first elements - of its second argument. - - &infix.ghci:type; - - Let's define a few variables in &ghci;. - - &infix.ghci:vars; - - If we call isPrefixOf using prefix - notation, we can have a hard time remembering which argument - we're checking for as a prefix of the other. - - &infix.ghci:prefix; - - But if we use infix notation, the code reads - more naturally; it's now obvious that we're checking the - variable on the left to see if it's a prefix of the variable on - the right. - - &infix.ghci:infix; - - There's no hard-and-fast rule that dictates when you ought - to use infix versus prefix notation, although prefix notation is - far more common. It's best to choose whichever makes your code - more readable in a specific situation. - - - The backtick notation is not a general mechanism: it's a - piece of special syntax that applies only to names. For - example, we can't put backticks around an expression that - returns a function, and then treat that as an infix - function. - - - hunk ./en/ch04-defining-types.xml 1194 + + + The case expression + + 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: this is the expression that we're + checking0. 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. These + expressions must all have the same type. The result of the + case expression is the result of the expression + associated with the first pattern to match. Matches are + attempted from top to bottom. + + To express here's the expression to + evaluate if none of the other patterns match, we just + use the wild card pattern _ as the last in our list + of patterns. + + + + Common beginner mistakes with patterns + + There are a few ways in which new Haskell + programmers can misunderstand or misuse patterns. Here are a + few potential missteps that you can easily avoid. + + Here are some attempts at pattern matching gone + awry. Depending on what you expect one of these examples to do, + it might contain a surprise.. + + + Matching against a variable + + &BogusPattern.hs:whichFruit; + + A naive glance suggests that this code is trying + to check the value f to see whether it + matches the value apple or + orange. + + The first appearance of apple + in the case expression is a pattern, + not a use of the top level + apple variable. Because this pattern + doesn't mention a type constructor or a literal value (the two + things that might cause a pattern matching attempt to fail), + this branch of the case will + always match, no matter what the value of + f is. + + + Irrefutable patterns + + We refer to a pattern that consists only of a + variable name, and thus cannot fail to match, as + irrefutable. The wild card pattern + _ is also irrefutable. + + + Here's a corrected version of this function. + + &BogusPattern.hs:betterFruit; + + Our bugfix involved making this function match against the + literal values "apple" and + "orange". + + + + Comparing values for equality + + What if we want to compare the values stored in two nodes + of type Tree, and return one of them if they're + equal? Here's a naive attempt. + + &BadTree.hs:nodesAreSame; + + A variable can only appear once in a function's + bindings. We can't place a variable in multiple positions to + express the notion this value and that should be + identical. Instead, we'll solve this problem using + guards, another invaluable language + feature. + + + + + Conditional evaluation with guards + + Pattern matching only lets us make fixed tests of + a value's shape. Although this is useful, we often need to + perform a more expressive check before evaluating a function's + body. Haskell provides a feature, guards, + that give us this ability. We'll introduce the idea with an + example. + + &Roots.hs:guardedRoots; + + A pattern can have zero or more guards, each an expression + of type Bool. A guard is introduced by a + | symbol, followed by the guard expression, then an + = symbol (or -> if we're in a + &case; expression), then the body to use if the guard succeeds. + If the pattern matches, each guard is evaluated in turn. If a + guard succeeds, its associated body is used as the result of the + function. If no guard succeeds, pattern matching moves on to + the next pattern. + + A guard expression can use any variables mentioned in the + pattern that it's associated with. The special-looking guard + expression otherwise is simply a variable + bound to the value True. + + Here's another example of guards in action, where we've + rewritten our nodesAreSame example to be + correct and concise. + + &GoodTree.hs:nodesAreSame; + + We can use guards anywhere that we can use + patterns. The advantage of writing a function as a series of + equations using pattern matching and guards is that it often + makes code much clearer. Remember the + myDrop function we defined in ? + + &myDrop.hs:myDrop.noid; + + Here's a reformulation that uses patterns and + guards. + + &myDrop.hs:niceDrop; + + This change in style lets us enumerate up + front the cases in which we expect a function to + behave differently. If we bury the decisions inside a function + as &if; expressions, the code becomes harder to read. + + + + Infix functions + + Usually, when we define or call a function in + Haskell, we write the name of the function, followed by its + arguments. This notation is referred to as + prefix, because the name of the function + comes before its arguments. If a function takes two arguments, + we have the option of using it in infix + form, where we place it between its first and second arguments. + This allows us to write expressions using functions as if they + were infix operators. + + To define or call a function using infix notation, + we enclose its name in backtick characters (sometimes known as + backquotes). Here's a simple infix definition. + + &Plus.hs:plus; + + Using infix notation doesn't change anything about + a function's behaviour. + + &infix.ghci:plus; + + Infix notation is useful for more than just our own + functions. For example, Haskell's standard + Data.List module defines a function, + isPrefixOf, that indicates whether all + elements of its first argument are equal to the first elements + of its second argument. + + To illustrate its use, let's begin by defining a + few variables in &ghci;. + + &infix.ghci:vars; + + If we call isPrefixOf using + prefix notation, it can be hard to remember which argument is + supposed to be the one we're checking. + + &infix.ghci:prefix; + + If we switch infix notation, the code + reads more naturally. It's now clearer that we're + checking the variable on the left to see if it's a prefix of the + variable on the right. + + &infix.ghci:infix; + + There's no hard-and-fast rule that dictates when you ought + to use infix versus prefix notation, although prefix notation is + far more common. It's best to choose whichever makes your code + more readable in a specific situation. + + + The backtick notation is not a general mechanism: it's a + piece of special syntax that applies only to names. For + example, we can't put backticks around an expression that + returns a function, and then treat that as an infix + function. + + hunk ./examples/ch03/myDrop.hs 19 -niceDrop n _ | n <= 0 = [] -niceDrop _ [] = [] -niceDrop n (_:xs) = niceDrop (n - 1) xs +niceDrop n xs | n <= 0 = xs +niceDrop _ [] = [] +niceDrop n (_:xs) = niceDrop (n - 1) xs hunk ./examples/ch04/BadTree.hs 1 +module BadTree where + +import Tree + +{-- snippet nodesAreSame --} +nodesAreSame (Node a _ _) (Node a _ _) = Just a +nodesAreSame _ _ = Nothing +{-- /snippet nodesAreSame --} hunk ./examples/ch04/BogusPattern.hs 1 -{-- snippet isHead --} -isHead f = case f of - head -> True - _ -> False -{-- /snippet isHead --} +{-- snippet whichFruit --} +data Fruit = Apple | Orange + +apple = "apple" + +orange = "orange" + +whichFruit :: String -> Fruit + +whichFruit f = case f of + apple -> Apple + orange -> Orange +{-- /snippet whichFruit --} + +{-- snippet betterFruit --} +betterFruit f = case f of + "apple" -> Apple + "orange" -> Orange +{-- /snippet betterFruit --} + hunk ./examples/ch04/GoodTree.hs 1 +module GoodTree where + +import Tree + +{-- snippet nodesAreSame --} +nodesAreSame (Node a _ _) (Node b _ _) + | a == b = Just a +nodesAreSame _ _ = Nothing +{-- /snippet nodesAreSame --} hunk ./examples/ch04/Roots.hs 51 - | n >= 0 && a /= 0 = Just (r1, r2) + | n >= 0 && a /= 0 = Just ((-b + s) / a2, (-b - s) / a2) hunk ./examples/ch04/Roots.hs 53 - where n = b**2 - 4 * a * c - a2 = 2 * a - r1 = (-b + sqrt n) / a2 - r2 = (-b - sqrt n) / a2 + where n = b^2 - 4 * a * c + a2 = 2 * a + s = sqrt n hunk ./examples/ch04/Roots.hs 65 - where n = b**2 - 4 * a * c + where n = b^2 - 4 * a * c hunk ./examples/ch04/Roots.hs 67 + s = sqrt n hunk ./examples/ch04/Roots.hs 71 - s = sqrt n - s' = sqrt n' + s' = sqrt n' hunk ./examples/ch04/Tree.hs 1 +module Tree where + }