Michael Saelee
Feb 20, 2019
Let’s start by implementing the following functions and looking for a pattern:
sum' :: (Num a) => [a] -> a
sum' [] = 0
sum' (x:xs) = x + sum' xs
product' :: (Num a) => [a] -> a
product' [] = 1
product' (x:xs) = x * product' xs
and' :: [Bool] -> Bool
and' [] = True
and' (x:xs) = x && or' xs
or' :: [Bool] -> Bool
or' [] = False
or' (x:xs) = x || or' xs
stringify :: (Show a) => [a] -> String
stringify [] = ""
stringify (x:xs) = show x ++ stringify xsEach of the above recursive functions has type [a] -> b, and is built around two basic pieces:
b) associated with the base case (the empty list)a) and combines it with the value returned by the recursive call (of type b) to compute the result (also of type b)I.e., to express a recursive list function of type [a] -> b, we need:
a -> b -> bbLet’s design a HOF that encapsulates this notion of primitive list recursion:
We call this function “fold” — specifically, “fold right”.
Note: foldr is actually defined for the “Foldable” type class — the list is an instance of Foldable. We’ll see how this works later!
Intuitively, foldr applies the combiner function f to the last (i.e., rightmost) value in the list and the base value first, then works outwards, applying f to each list value and the result from the previous application of f, in turn.
E.g., foldr f z [1..5] = (1 f (2 f (3 f (4 f (5 f z)))))
                   = (f 1 (f 2 (f 3 (f 4 (f 5 z)))))
Let’s define the above recursive functions in terms of foldr:
sum'' :: (Num a) => [a] -> a
sum'' = foldr (+) 0
product'' :: (Num a) => [a] -> a
product'' = foldr (*) 1
or'' :: [Bool] -> Bool
or'' = foldr (||) False
and'' :: [Bool] -> Bool
and'' = foldr (&&) True
stringify' :: (Show a) => [a] -> String
stringify' = foldr ((++) . show) ""Try a few others:
(+++) :: [a] -> [a] -> [a]
l1 +++ l2 = foldr (:) l2 l1
length' :: [a] -> Int
length' = foldr (\_ r -> succ r) 0And higher order functions:
map' :: (a -> b) -> [a] -> [b]
map' f = foldr ((:) . f) []
filter' :: (a -> Bool) -> [a] -> [a]
filter' f = foldr iter []
  where iter x rst | f x = x : rst
                   | otherwise = rstHow about reverse?
Which translates into:
But this is inefficient! (Why?)
A better implementation of reverse is:
reverse''' :: [a] -> [a]
reverse''' xs = recur [] xs
  where recur ys [] = ys
        recur ys (x:xs) = recur (x:ys) xsNote how the result (ys) computed by recur is built up (aka “accumulated”) from left to right over the input list. It is difficult (but not impossible!) to implement reverse''' in terms of foldr.
In a left fold, we want to start by applying the combiner function to the base value and the first value in the list, then proceed onto succeeding values, so:
foldl f z [1..5] = (((((z f 1) f 2) f 3) f 4) f 5)
             = (f (f (f (f (f z 1) 2) 3) 4) 5)
Determine the type of the left fold function and implement it:
Define reverse using foldl:
Which folds (if any) work with infinite lists?
Try:
Why?
Intuitively: - foldr’s combining function is applied to each element in turn (and to the recursive call) — this lets the combining function “short circuit” early - foldl builds up an accumulated value; the result is not known until all recursions are evaluated - foldl must recurse through the entire list to build up all the function applications before evaluating the outermost one
consider:
Following are true:
But following are not:
- is left associative, and so should be evaluated using the left-fold.
^ is right associative, and should be evaluated using the right-fold.