-- First class functions -- Anonymous functions h1 x = x + 4 h2 = \ x -> x + 4 -- nested functions map1 (g,u) = let f [] = [] f (x:xs) = (g x) : (f xs) in f u {- This acts similarly to other nested declarations: Parameters and local variables of outer functions are visible within inner functions (using lexical scoping rules). Purpose: localize scope of nested functions, and avoid the need to pass auxiliary parameters defined in outer scopes. Semantics of a function definition now depend on values of function’s free variables . -} -- FUNCTIONS AS RETURN VALUES map2 g = let f [] = [] f (x:xs) = (g x) : (f xs) in f {- In fact, we can simplify still further by rewriting map to return the nested function "f" Now we need a slightly different calling convention, passing the two arguments separately: -} inc x = x+1 w1 = ((map2 inc) [1,2,3]) -- yields [2;3;4] -- But now we can also choose to pass just one argument at a time minc = map2 inc w2 = minc [1,2,3] w3 = minc [4,5,6] -- Curried functions take multiple arguments -- but we can apply the function to fewer arguments map3 g u = let f [] = [] f (x:xs) = (g x): (f xs) in f u w4 = map3 inc [2,5,7] -- function application associates to the left, so w5 = (map3 inc) [4,7,8] {- Notice difference in types map1 :: (t -> a, [t]) -> [a] map3 :: (t -> a) -> [t] -> [a] The function aroow associates to the right so map3:: (t -> a) -> ([t] -> [a]) Notice curried functions return functions! -} -- Currying is most often useful when passing partially -- applied functions to other higher-order functions: pow n b = if n==0 then 1 else b * (pow (n-1) b) w6 = map (pow 3) [1,2,3] -- evaluates to [1,8,27] -- we can store functions in data structures -- we can create a list of functions w7 = map pow [0,1,2,3,4] [f0,f1,f2,f3,f4] = w7 -- try applying f0,f1 etc. to 2, or 5 -- Anonymous functions w8 = map (\ x -> x+5) [2,5,7] {- Name derives from the Alonzo Church’s “lambda calculus,” or iginally invented to study computability theory, in which anonymous functions would be written using the Greek lambda character. -} -- Lets capture another pattern (other than map) -- Add a list of integers sum2 [] = 0 sum2 (x:xs) = x + sum2 xs -- multiply a list of integers prod2 [] = 1 prod2 (x:xs) = x * prod2 xs w9 = sum2 [3,4,6] w10 = prod2 [2,6,3] -- Here are a few more copy [] =[] copy (x:xs) = x : copy xs len [] = 0 len (x:xs) = 1 + len xs -- This function is called foldr in Haskell -- I called it fold, so that it doesn't conflict. fold f n [] = n fold f n (x:xs) = f x (fold f n xs) sum3 xs = fold (\ x y -> x+y) 0 xs prod3 xs = fold (\ x y -> x*y) 1 xs copy3 xs = fold (\ x y -> x : y) [] xs len3 xs = fold (\ x y -> 1 + y) 0 xs elem3 x xs = fold (\ item ans -> item==x || ans) False xs {- Function foldr computes a value working from the tail of the list to the head (from right to left). Argument n is the value to return for the empty list. Argument f is the (Curried) function to apply to each element and the previously computed result. foldr (+) 8 (x1 : x2 : x3 : x4 : []) x1 + x2 + x3 + x4 + 8 f x1 (f x2 (f x3 (f x4 8))) -} -- Accumulating functions -- Recall -- len [] = 1 -- len (x:xs) = 1 + len xs -- Note that after the recursive call, we still need to add 1. len4 [] n = n len4 (x:xs) n = len4 xs (1+n) len5 xs = len4 xs 0 -- note we need to intialize the acc prod4 [] n = n prod4 (x:xs) n = prod4 xs (x*n) prod5 xs = prod4 xs 1 -- here intitialization is 1 -- We might do this because these functions are -- tail recursive and can be efficiently compiled! -- Another way to write tail recursive functions -- is to use continuation style. Every function gets -- an extra argument len6 :: [a] -> (Int -> b) -> b len6 [] k = k 0 len6 (x:xs) k = len6 xs (\ n -> k (n+1)) w12 = len6 [2,5,7] id prod6 [] k = k 1 prod6 (x:xs) k = prod6 xs (\ n -> k (x*n)) {- Notice that every call is now a tail-call . Note too that len6 only returns after the outer k is invoked; in essence, it needn’t return at all. If it were the whole program, it wouldn’t need to return at all. This is because k is serving the same role as a return address: saying “what to do n ext.” This means we can evaluate len6 without a stack ! Functions like k are called continuations and programs written using them are said to be in continuation-passing style (CPS) We may choose to write (parts or all of) programs explicitly in CPS because it makes it easy to express a particular algorithm or because it clarifies the control structure of the program. Note that CPS programs are just a subset of ordinary function al programs that happens to make heavy use of the (existing) enormous power of first-class functions. Remarkably, we can also systematica lly convert any functional program into an equivalent CPS program. (Details omitted.) -} --