[More refactoring of chapter 4. Bryan O'Sullivan **20080118015851] { hunk ./en/ch04-defining-types.xml 174 + + Tuples, algebraic data types, and when to use + each + + There is some overlap between tuples and user-defined + algebraic data types. If we wanted to, we could represent our + BookSection type from earlier as an (Int, + String) pair. + + &booksection.ghci:tuple; + + An algebraic data type gives us additional discrimination: + two tuples with elements of the same type have the same type, + but two algebraic data types with elements of the same type + have distinct types. This lets us bring the type system to + bear in writing programs with fewer bugs. Consider the + following representations of a two-dimensional vector. + + &AlgebraicVector.hs:types; + + Because Vector2D and Polar2D are + distinct types, we can't accidentally mix up our + representations. + + &algebraicvector.ghci:typed; + + If we used tuples to represent these values, we could + quickly land ourselves in hot water by mixing the two + representations inappropriately. + + &algebraicvector.ghci:tupled; + + The type system can't rescue us here, because as far as + it's concerned, we're comparing two (Double, + Double) pairs, which is a valid expression. + + There's no hard and fast rule for deciding when it's + better to use a tuple or a distinct data type, but here's a + rule of thumb to follow. + + If you're building and mixing many compound values that + will have long lives and propagate widely throughout your + code, the added type safety will almost certainly pay off by + enlisting the compiler to prevent bugs. For smaller, + localised uses, such as returning multiple values from a + function and immediately consuming them, a tuple is + fine. + + In general, as the cost of making a mistake, or locating a + bug, grows, then increasing type safety makes more sense. + Better yet, stronger typing can often add little or no cost in + either readability or performance. + + hunk ./en/ch04-defining-types.xml 245 - together. It corresponds to a struct in C or - C++, and its components correspond to the fields of a - struct. Here's a C equivalent of the - BookSection type that we defined - earlier. + together into a compound value. It corresponds to a + struct in C or C++, and its components + correspond to the fields of a struct. Here's a + C equivalent of the BookSection type that we + defined earlier. hunk ./en/ch04-defining-types.xml 357 + + Pattern matching + + Now that we've seen how to construct values with + algebraic data types, let's discuss how we work with them. We + need to be able to distinguish between cases, and to extract + values from a compound value. Haskell has a simple + pattern matching facility that lets us do + both of these. + + 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 that we define are already + patterns. We're just adding to our existing knowledge + here. + + Here's an example of pattern matching in action on + a list; we're going to add all elements of the list + together. + + &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, over + different inputs. (By the way, there's already a standard + function, sum, that does this + adding-of-a-list for us. This sumList is + purely for illustration.) + + 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 + (True and 5 in the tuple + pattern above), 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. + + And for good measure, here's how we can define functions to + access the fields of the BookSection type we defined in + . + + &BookSection.hs:accessors; + + + The ordering of patterns is important + + 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. + + + Here are some rules of thumb to help with remembering how + pattern matching works. A constructor in a pattern + checks that the matched value has the right shape. A + literal value ensures that that portion of the value has exactly + the matching contents. And a variable makes no assertions about + either the shape or contents of the matched value; it matches anything, + and gives the variable that value. + + So the pattern (3:xs) first of all is an + assertion that a matching value is a non-empty list, by matching + against the (:) constructor. It also + ensures that the head of the list is the literal value + 3. And whatever the tail of the list is, it + will be bound to the variable xs. + + + 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. + + 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. + + 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. + + Wild cards also help readability, as they make it easier + to tell which values we're really using. + + &BookSection.hs:niceAccessors; + + + + hunk ./en/ch04-defining-types.xml 975 - - 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. - - 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. - - Here's an example of pattern matching in action on a list; - we're going to add all elements of the list together. - - &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, over - different inputs. (By the way, there's already a standard - function, sum, that does this - adding-of-a-list for us. This sumList is - purely for illustration.) - - 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 - (True and 5 in the tuple - pattern above), 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. - - And for good measure, here's how we can define functions to - access the fields of the BookSection type we defined in - . - - &BookSection.hs:accessors; - - - The ordering of patterns is important - - 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. - - - Here are some rules of thumb to help with remembering how - pattern matching works. A constructor in a pattern - checks that the matched value has the right shape. A - literal value ensures that that portion of the value has exactly - the matching contents. And a variable makes no assertions about - either the shape or contents of the matched value; it matches anything, - and gives the variable that value. - - So the pattern (3:xs) first of all is an - assertion that a matching value is a non-empty list, by matching - against the (:) constructor. It also - ensures that the head of the list is the literal value - 3. And whatever the tail of the list is, it - will be bound to the variable xs. + + The case expression hunk ./en/ch04-defining-types.xml 978 - - 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. - - 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. - - 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. - - Wild cards also help readability, as they make it easier - to tell which values we're really using. - - &BookSection.hs:niceAccessors; - - - - - 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. + 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. hunk ./en/ch04-defining-types.xml 984 - 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. + 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. hunk ./en/ch04-defining-types.xml 990 - 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. + 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. hunk ./en/ch04-defining-types.xml 997 - 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. - + 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. hunk ./en/ch04-defining-types.xml 1005 - 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. + 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/ch04-defining-types.xml 1012 - - - Early pattern matching pitfalls - - 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. + + + + Common beginner mistakes with patterns hunk ./en/ch04-defining-types.xml 1017 - 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. + 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. hunk ./en/ch04-defining-types.xml 1021 - 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. + 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. hunk ./en/ch04-defining-types.xml 1026 - &BogusPattern.hs:isHead; + 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. hunk ./en/ch04-defining-types.xml 1030 - 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. + &BogusPattern.hs:isHead; hunk ./en/ch04-defining-types.xml 1032 - 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. + 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. hunk ./en/ch04-defining-types.xml 1037 - - Irrefutable patterns + 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. hunk ./en/ch04-defining-types.xml 1045 - 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. - + + Irrefutable patterns hunk ./en/ch04-defining-types.xml 1048 - 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. + 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. + hunk ./en/ch04-defining-types.xml 1055 - 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 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. hunk ./en/ch04-defining-types.xml 1067 - 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. + 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. hunk ./en/ch04-defining-types.xml 1073 - + 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. hunk ./en/ch04-defining-types.xml 1218 - addfile ./examples/ch04/AlgebraicVector.hs hunk ./examples/ch04/AlgebraicVector.hs 1 +{-- snippet types --} +-- X and Y coordinates. +data Vector2D = Vector2D Double Double + +-- Angle and distance. +data Polar2D = Polar2D Double Double +{-- /snippet types --} addfile ./examples/ch04/algebraicvector.ghci hunk ./examples/ch04/algebraicvector.ghci 1 +:load AlgebraicVector + +--# typed +Vector2D (sqrt 2) (sqrt 2) == Polar2D (pi / 4) 2 + +--# tupled +(sqrt 2, sqrt 2) == (pi / 4, 2) hunk ./examples/ch04/booksection.ghci 24 +--# tuple + +BookSection 4 "Not being Mrs. Grundy, who *was* Mr. Bounderby?" +(4, "Not being Mrs. Grundy, who *was* Mr. Bounderby?") + }