----------------------------------------------------------------------- -- Haskell: The Craft of Functional Programming -- Simon Thompson -- (c) Addison-Wesley, 1999. -- -- Chapter 10 ----------------------------------------------------------------------- -- Functions as values -- ^^^^^^^^^^^^^^^^^^^ module Chapter10 where import Prelude hiding (succ,curry,uncurry,flip) import Pictures hiding (flipH,rotate,flipV,sideBySide,invertColour, superimpose,printPicture) import Chapter9 hiding (concat,map,filter,zipWith,and,foldr1,foldr, doubleAll,flipV,sideBySide,getWord) import qualified Chapter7 -- A fixity declaration for the forward composition operator, >.> infixl 9 >.> -- Function-level definitions -- ^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Revisiting the Picture example rotate :: Picture -> Picture rotate = flipV . flipH flipH :: Picture -> Picture flipH = reverse -- Function composition and forward composition -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- A composition operator taking its arguments in the opposite order to `.'. (>.>) :: (a -> b) -> (b -> c) -> (a -> c) g >.> f = f . g -- Another definition of rotate using >.> rotate' = flipH >.> flipV -- Functions as values and results -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Compose a function with itself: apply it twice, in other words. twice :: (a -> a) -> (a -> a) twice f = (f . f) succ :: Int -> Int succ n = n+1 -- We can generalize twice so that we pass a parameter giving the number -- of times the functional argument is to be composed with itself: iter :: Int -> (a -> a) -> (a -> a) iter n f | n>0 = f . iter (n-1) f | otherwise = id -- An alternative definition of iter: iter' n f = foldr (.) id (replicate n f) -- Expressions defining functions -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- A function returning a function, namely the function to `add n to its -- argument'. addNum :: Int -> (Int -> Int) addNum n = addN where addN m = n+m -- Lambda notation -- ^^^^^^^^^^^^^^^ -- An alternative definition of addNum using a lambda expression. addNum' :: Int -> (Int -> Int) addNum' n = (\m -> n+m) -- The `plumbing' function: comp2 :: (a -> b) -> (b -> b -> c) -> (a -> a -> c) comp2 f g = (\x y -> g (f x) (f y)) -- Using the `plumbing' function plumbingExample = comp2 sq add 3 4 where sq x = x*x add y z = y+z -- Partial Application -- ^^^^^^^^^^^^^^^^^^^ -- The function multiply multiplies together two arguments. multiply :: Int -> Int -> Int multiply x y = x*y -- Double all elements of an integer list. doubleAll :: [Int] -> [Int] doubleAll = map (multiply 2) -- Another definition of addNum, using partial application to achieve the -- `function as result'. addNum'' n m = n+m -- Revisiting the Pictures example, yet again. flipV :: Picture -> Picture flipV = map reverse sideBySide :: Picture -> Picture -> Picture sideBySide = zipWith (++) -- An example function of type (Int -> Int) -> Int g :: (Int -> Int) -> Int g h = (h 0) + (h 1) -- How many arguments do functions have? -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Three examples from the text processing functions first seen in Chapter 7. dropSpace = dropWhile (member whitespace) dropWord = dropWhile (not . member whitespace) getWord = takeWhile (not . member whitespace) -- Auxiliary definitions ... member xs x = elem x xs -- Operator Sections -- ^^^^^^^^^^^^^^^^^ -- Example of a function defined using partial application and operator sections. egFun :: [Int] -> [Int] egFun = filter (>0) . map (+1) -- Revisiting the Picture example -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Some of the functions are already (re)defined in this script. -- Among the other functions mentioned were invertColour :: Picture -> Picture invertColour = map (map invert) superimpose :: Picture -> Picture -> Picture superimpose = zipWith (zipWith combineChar) -- The definition of combineChar is left as an exercise: it's a dummy definition -- here. combineChar :: Char -> Char -> Char combineChar = combineChar -- Printing a picture: uses putStr after a newline has been added at the end of -- every line and the lines are joined into a single string. printPicture :: Picture -> IO () printPicture = putStr . concat . map (++"\n") -- Further examples -- ^^^^^^^^^^^^^^^^ -- Revisiting earlier examples ... -- Double all integers in a list, doubleAll' :: [Int] -> [Int] doubleAll' = map (*2) -- get the even numbers in a list of integers, getEvens :: [Int] -> [Int] getEvens = filter ((==0).(`mod` 2)) -- get a word from the start of a string. getWord' = getUntil (`elem` whitespace) -- Currying and uncurrying -- ^^^^^^^^^^^^^^^^^^^^^^^ -- An uncurried function to multiply together the two itegers in a pair. multiplyUC :: (Int,Int) -> Int multiplyUC (x,y) = x*y -- Turn an uncurried function into a curried version, curry :: ((a,b) -> c) -> (a -> b -> c) curry g x y = g (x,y) -- and vice versa. uncurry :: (a -> b -> c) -> ((a,b) -> c) uncurry f (x,y) = f x y -- Change the order of arguments of a two argument curried function. flip :: (a -> b -> c) -> (b -> a -> c) flip f x y = f y x -- Example: creating an index -- ^^^^^^^^^^^^^^^^^^^^^^^^^^ -- The basic type symonyms type Doc = String type Line = String type Word = String -- The type of the top-level function makeIndex :: Doc -> [ ([Int],Word) ] -- The top-level definition makeIndex = lines >.> -- Doc -> [Line] numLines >.> -- [Line] -> [(Int,Line)] allNumWords >.> -- [(Int,Line)] -> [(Int,Word)] sortLs >.> -- [(Int,Word)] -> [(Int,Word)] makeLists >.> -- [(Int,Word)] -> [([Int],Word)] amalgamate >.> -- [([Int],Word)] -> [([Int],Word)] shorten -- [([Int],Word)] -> [([Int],Word)] -- Implementing the component functions -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Attach a number to each line. numLines :: [Line] -> [ ( Int , Line ) ] numLines linels = zip [1 .. length linels] linels -- Associate each word with a line number numWords :: ( Int , Line ) -> [ ( Int , Word ) ] numWords (number , line) = [ (number , word) | word <- Chapter7.splitWords line ] -- The definition uses splitWords from Chapter 7, modified to use a different -- version of whitespace. For this to take effect, need to make the modification -- in the Chapter7.lhs file. whitespace :: String whitespace = " \n\t;:.,\'\"!?()-" -- Apply numWords to each integer,line pair. allNumWords :: [ ( Int , Line ) ] -> [ ( Int , Word ) ] allNumWords = concat . map numWords -- The list must next be -- sorted by word order, and lists of lines on which a word appears be built. -- The ordering relation on pairs of numbers and -- words is given by orderPair :: ( Int , Word ) -> ( Int , Word ) -> Bool orderPair ( n1 , w1 ) ( n2 , w2 ) = w1 < w2 || ( w1 == w2 && n1 < n2 ) -- Sorting the list using the orderPair ordering on pairs. sortLs :: [ ( Int , Word ) ] -> [ ( Int , Word ) ] sortLs [] = [] sortLs (p:ps) = sortLs smaller ++ [p] ++ sortLs larger where smaller = [ q | q<-ps , orderPair q p ] larger = [ q | q<-ps , orderPair p q ] -- The entries for the same word need to be accumulated together. -- First each entry is converted to having a list of line numbers associated with -- it, thus makeLists :: [ (Int,Word) ] -> [ ([Int],Word) ] makeLists = map mklis where mklis ( n , st ) = ( [n] , st ) -- After this, the lists associated with the same words are amalgamated. amalgamate :: [ ([Int],Word) ] -> [ ([Int],Word) ] amalgamate [] = [] amalgamate [p] = [p] amalgamate ((l1,w1):(l2,w2):rest) | w1 /= w2 = (l1,w1) : amalgamate ((l2,w2):rest) | otherwise = amalgamate ((l1++l2,w1):rest) -- Remove all the short words. shorten :: [([Int],Word)] -> [([Int],Word)] shorten = filter sizer where sizer (nl,wd) = length wd > 3 -- Verification and general functions -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- All the functions used in this section have been defined earlier.