Topic: Polymorphic and Higher-Order Functions Date: Oct. 2, 2009 Number: 6 Examples: hof.hs Reading: Chap. 5 For next time: SA 4 (written, not programming) Read Chap. 9. -- Drawing Shapes Finish drawing shapes from previous lecture notes -- Polymorphic Types What is the type of zip? Or length? Or head and tail? Haven't talked about that. It's time. We saw a function to compute the length of a string in recursiveExamples.hs: -- Computes the length of a string strLength :: [Char] -> Int strLength [] = 0 strLength (c:rest) = 1 + strLength rest But no reason that it must be [Char]. This code does not depend on the type of the list. Function length takes the length of ANY list. How do we say that? length :: [a] -> Int (book says Integer, but Int is correct) What is a? A type variable. Matches any type. Traditional to use a, b, c if no better names come to mind. So in Prelude can find something like: length :: [a] -> Int length [] = 0 length (x:xs) = 1 + length xs Convention: x is a single element xs is a list of elements xss is a list of lists ---- What about head, tail? head :: [a] -> a tail :: [a] -> [a] Implement? head (x:_) = x tail (_:xs) = xs ---- Saw zip last time, zips up ordered pairs. Looked at: zip colorList shapeList so type is: [Color] -> [Shape] -> [(Color, Shape)] More generally: zip :: [a] -> [b] -> [(a,b)] note each list can be a different type, but the order pair will have its first element the same type as the first list, second matches second list. How to write zip if it weren't supplied? zip (x:xs) (y:ys) = (x, y) : zip xs ys zip _ _ = [] (so uneven lists, the longer one gets truncated). ---- Map takes a function. How do we deal with that? map :: (a -> b) -> [a] -> [b] The function takes a's into b's, so the list of a's is converted into a list of b's. zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] The funtion takes two arb. types, returns a third. Implementations: map f (x:xs) = f x : map f xs map f [] = [] zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys zipWith _ _ _ = [] ---- The fold functions are interesting. We usually used it as: foldl (+) 0.0 list so it looks like it should be: foldl :: (a -> a -> a) -> a -> [a] -> a But foldl is really more general that that. Why can't the function be: (a -> b -> ?) The ? must be a, because the folded thing always FIRST argument to the function: foldl (+) 0 [1,2,3,4] => ((((0 + 1) + 2) + 3 ) + 4) So initial value must be a, list type b, return type a. Gives foldl :: (a -> b -> a) -> a -> [b] -> a How about foldr? foldr (+) 0 [1,2,3,4] => (1 + (2 + (3 + (4 + 0)))) So if the function takes (a -> b -> ?) the ? must be b, because will always be second. So foldr :: (a -> b -> b) -> b -> [a] -> b This "least general type that includes all cases" is called PRINCIPAL TYPE. Less general than, say, (a -> b -> c) -> d -> [e] -> f All foldl and folr instances would fit this pattern, but many things that fit this pattern would not work for foldl or foldr. How implement these: foldr is easy: foldr op init [] = init foldr op init (x:xs) = x `op` foldr op init xs foldl is a bit trickier: foldl op init [] = init foldl op init (x:xs) = foldl op (init `op` x) xs What is going on here? We basically start by taking the first thing and combining it with init: init `op` x. But that then becomes the NEW init value in the recursive call! So get (... ((init `op` x1) `op` x2) ...) `op` xn Aside - why would anyone want to use foldl or foldr this with two-type function? Strange identity: rebuild list = foldr (:) [] list => list What is pricipal type of rebuild? Note function passed to foldr is: (:) :: a -> [a] -> [a] Because principal type of foldr is foldr :: (c -> b -> b) -> b -> [c] -> b (I substituted c for a in the definition above, so I won't get a name conflict in the subsequent derivation.) ) Matching the principal type for (:) to (c -> b -> b) gives me: c = a and b = [a]. so have type of list is [c] = [a], return type b = [a]. Verify that type of init value [] is b = [a]. rebuild :: [a] -> [a] Another example: concat x = foldr (++) [] x The principal type for (++) :: [a] -> [a] -> [a] Using foldr :: (c -> b -> b) -> b -> [c] -> b again, see that c = [a] and b = [a] Note that [] is type [a], which is good! So concat :: [[a]] -> [a] Another example: suppose want to convert [1, 2, 3, 4] to [[1, 2, 3, 4], [2, 3, 4], [3, 4], [4], []] suffixes xs = foldr consToHead [[]] xs where consToHead x list = (x : head list) : list Type signatures for these? Homework.