Upcoming SlideShare
×

# Real World Haskell: Lecture 2

1,855 views
1,745 views

Published on

Published in: Technology, Education
3 Likes
Statistics
Notes
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

Views
Total views
1,855
On SlideShare
0
From Embeds
0
Number of Embeds
50
Actions
Shares
0
77
0
Likes
3
Embeds 0
No embeds

No notes for slide

### Real World Haskell: Lecture 2

1. 1. Real World Haskell: Lecture 2 Bryan O’Sullivan 2009-10-14
2. 2. My homework, using only concepts from last week import Data . L i s t ( i s I n f i x O f ) pattern = ” toast ” f o r m a t number l i n e = show number ++ ” : ” ++ l i n e g r e p number i n p u t = i f null input then [ ] e l s e i f i s I n f i x O f p a t t e r n ( head i n p u t ) then f o r m a t number ( head i n p u t ) : g r e p ( number + 1 ) ( t a i l i n p u t ) else g r e p ( number + 1 ) ( t a i l i n p u t ) grepFromOne i n p u t = u n l i n e s ( g r e p 1 ( l i n e s i n p u t ) ) main = i n t e r a c t grepFromOne
3. 3. Wasn’t Haskell supposed to be “pretty”? That grep function sure didn’t look pretty to me! But what, speciﬁcally, is ugly about it? We repeat ourselves, using head and tail twice. There’s a mess of nested if /else badness going on.
4. 4. Lists, revisited There are two ways to construct a list: An empty list [] A non-empty list ﬁrstElement : restOfList We refer to [] and : as list constructors, since they construct list values.
5. 5. Lists, constructed Knowing about these constructors, how might we construct a 4-element list?
6. 6. Lists, constructed Knowing about these constructors, how might we construct a 4-element list? 1 : 2 : 3 : 4 : [] The bracketed notation we saw last week is syntactic sugar for the form above. In other words, any time you see this: [1,2,3,4] You can read it as this, and vice versa: 1 : 2 : 3 : 4 : []
7. 7. Lists, misconstrued Beginner mistake alert: A list must end with an empty list. So a construction like this makes no sense: ’a’ : ’b’ : ’c’ How would we ﬁx it up? ’a’ : ’b’ : ’c’ : []
8. 8. Back to our roots Remember the fragment of square root code from last week? oneRoot a b c = (−b + ( b ˆ2 + 4∗ a ∗ c ) ) / ( 2 ∗ a ) If we pass in a value of zero for a, the root is undeﬁned, since we’d be dividing by zero. oneRoot a b c = i f a == 0 then (−b + ( b ˆ2 − 4∗ a ∗ c ) ) / (2∗ a ) e l s e e r r o r ” d i v i d e by z e r o ! ”
9. 9. But... I don’t like that if , because how would we write this using mathematical notation? −b ± (b 2 − 4ac) roots(a, b, c) = if a = 0 2a = undeﬁned otherwise And . . . isn’t Haskell supposed to be mathematically inspired?
10. 10. Introducing guards A guard is a Boolean expression preceded by a vertical bar character. oneRoot a b c | a /= 0 = (−b + ( b ˆ2 − 4∗ a ∗ c ) ) / ( 2 ∗ a ) | o t h e r w i s e = e r r o r ” d i v i d e by z e r o ” Guards are evaluated in top-to-bottom order. For the ﬁrst one that evaluates to True, the expression on the right of the = sign is used as the result of the function. The name otherwise is simply another name for True.
11. 11. Using guards Here’s a second attempt at our grep function, this time using guarded expressions: g r e p number i n p u t | null input = [] | i s I n f i x O f p a t t e r n ( head i n p u t ) = f o r m a t number ( head i n p u t ) : g r e p ( number + 1 ) ( t a i l i n p u t ) | otherwise = g r e p ( number + 1 ) ( t a i l i n p u t )
12. 12. How did this help? We got rid of the nested if expressions, and our “ﬂatter” code is easier to follow. It’s still fugly and repetitive, though. What about head and tail ?
13. 13. Pattern matching When we construct a list, the Haskell runtime has to remember what constructors we used. It goes a step further, and makes this information available to us. We can examine the structure of a piece of data at runtime using pattern matching.
14. 14. Pattern matching on an empty list What’s the length of an empty list? myLength [ ] = 0 This is a function of one argument. If that argument matches the empty-list constructor, our function returns the value 0.
15. 15. Pattern matching on a non-empty list What’s the length of a non-empty list? myLength ( x : x s ) = 1 + myLength x s If our argument matches the non-empty-list constructor “:”, then: the head of the list is bound to the name x; the tail to xs; and the expression is returned with those bindings.
16. 16. Aaaand it’s over to you Now that we know how pattern matching works, let’s do some super-simple exercises: Write versions of the head and tail functions: head [1 ,2 ,3] == > 1 tail [ ’a’ , ’b’ , ’c ’] == > [ ’b’ , ’c ’] Give your versions diﬀerent names, or you’ll have a hard time trying them out in ghci.
17. 17. Matching alternative patterns We combine our two pattern matches into one function deﬁnition by writing them one after the other: myLength [ ] = 0 myLength ( x : x s ) = 1 + myLength x s As with guards, pattern matching proceeds from top to bottom and stops at the ﬁrst success. The RHS of the ﬁrst pattern that succeeds is used as the body of the function.
18. 18. Matching alternative patterns We combine our two pattern matches into one function deﬁnition by writing them one after the other: myLength [ ] = 0 myLength ( x : x s ) = 1 + myLength x s As with guards, pattern matching proceeds from top to bottom and stops at the ﬁrst success. The RHS of the ﬁrst pattern that succeeds is used as the body of the function. Question: What do you suppose happens if no pattern matches?
19. 19. Over to you, part two And now that we know how to write function deﬁnitions that can deal with multiple patterns, another exercise: Write a version of the take function: take 3 [100 ,200 ,300 ,400 ,500] ==> [100 ,200 ,300] take 3 [ ’a’ , ’b ’] ==> [ ’a’ , ’b ’] take 3 [] ==> ???
20. 20. Over to you, part two And now that we know how to write function deﬁnitions that can deal with multiple patterns, another exercise: Write a version of the take function: take 3 [100 ,200 ,300 ,400 ,500] ==> [100 ,200 ,300] take 3 [ ’a’ , ’b ’] ==> [ ’a’ , ’b ’] take 3 [] ==> ??? Now use ghci to ﬁgure out what the drop function does, and write a version of that.
21. 21. Metasyntactic variables Languages have their cultural habits, and Haskell is no exception. You’ll very often see the names used when pattern matching a list follow a naming convention like this: (x: xs) (y: ys) (d:ds) and so on. Think of the “s” suﬃx as “pluralizing” a name, so “x” (ex) is the head of the list, and “xs” (exes) is the rest.
22. 22. Matching multiple patterns We can match more than one pattern at a time. Consider how we might add the elements of two vectors, represented as lists: sumVec ( x : x s ) ( y : y s ) = x + y : sumVec x s y s sumVec [ ] [] = []
23. 23. Combining pattern matching and guards Things start to get seriously expressive when we combine language features. Remember that bloated grep deﬁnition from earlier? Let’s put our new friends to work! grep n [ ] = [] grep n ( x : xs ) | i s I n f i x O f pattern x = format n x : g r e p ( n+1) x s | otherwise = g r e p ( n+1) x s
24. 24. What’s happening here? When we deﬁne a function, a pattern binds names to values. Given a list and a pattern (x: xs), if the list is non-empty, then x is bound to its head, and xs to its tail. Then each guard (if any) associated with that pattern is evaluated in turn, with those bindings in eﬀect, until a guard succeeds. Once a guard succeeds, its RHS is used as the result, with the bindings from that pattern still in eﬀect. If the pattern match fails, or no guard succeeds, we fall through to the next pattern and its guards.
25. 25. What’s happening here? When we deﬁne a function, a pattern binds names to values. Given a list and a pattern (x: xs), if the list is non-empty, then x is bound to its head, and xs to its tail. Then each guard (if any) associated with that pattern is evaluated in turn, with those bindings in eﬀect, until a guard succeeds. Once a guard succeeds, its RHS is used as the result, with the bindings from that pattern still in eﬀect. If the pattern match fails, or no guard succeeds, we fall through to the next pattern and its guards. Note: If all patterns and guards in a function deﬁnition were to fail on some input, we’d get a runtime error. That would be bad.
26. 26. And speaking of bad. . . Remember our sumVec function? sumVec ( x : x s ) ( y : y s ) = x + y : sumVec x s y s sumVec [ ] [] = [] What happens if we apply this to lists of diﬀerent lengths? sumVec [1,2,3] [4,5,6,7,8]
27. 27. And speaking of bad. . . Remember our sumVec function? sumVec ( x : x s ) ( y : y s ) = x + y : sumVec x s y s sumVec [ ] [] = [] What happens if we apply this to lists of diﬀerent lengths? sumVec [1,2,3] [4,5,6,7,8] So . . . what can we do about that exciting behaviour?
28. 28. One possible response Let’s declare that the sum of two vectors should end when we reach the end of the shorter vector. sumVec ( x : x s ) ( y : y s ) = x + y : sumVec x s y s sumVec what ever = [] Whoa, dude. . . Why does this work?
29. 29. One possible response Let’s declare that the sum of two vectors should end when we reach the end of the shorter vector. sumVec ( x : x s ) ( y : y s ) = x + y : sumVec x s y s sumVec what ever = [] Whoa, dude. . . Why does this work? The names “what” and “ever” are patterns. However, a plain name (with no constructors in sight) does not inspect the structure of its argument. So “what” and “ever” will each happily match either an empty or a non-empty list.
30. 30. An aside: strings are lists In Haskell, we write characters surrounded by single quotes, and strings in double quotes. Strings are lists, so: ” abc ” is syntactic sugar for [ ’ a ’ , ’b ’ , ’c ’ ] and hence for ’a ’ : ’b ’ : ’c ’ : [ ] Functions that can manipulate lists can thus manipulate strings. Oh, and escape sequences such as ”rnt” work, too.
31. 31. We are not limited to one constructor per pattern Suppose we want to squish consecutive repeats of an element in a list. compress ” f o o o b a r r r r r r ” == ” f o b a r ” > We can write a function to do this using an elegant combination of pattern matching and guards: compress ( x : y : ys ) | x == y = compress ( y : ys ) | otherwise = x : compress ( y : ys ) compress ys = ys Notice that our pattern matches on two consecutive list constructors!
32. 32. Homework Write a function that returns the nth element of a list, counting from zero. nth 2 ” squeak ” == ’ u ’ > Write a function that returns the element immdiately before the last element of a list. lastButOne [1 ,2 ,3 ,4 ,5] == 4 > Write a function that determines whether its input is a palindrome. isPalindrome ” foobar ” == F a l s e > isPalindrome ” foobarraboof ” == True >