Joseph Haugh
University of New Mexico
What does this expression evaluate to?
map fst [(1,2), (3,4), (5,6)]
[1,3,5]
What does this expression evaluate to?
map (filter even) [[1,2,3], [4,5,6], [7,8,9]]
[[2], [4,6], [8]]
What does this expression evaluate to?
filter (any odd) [[1,2,3], [4,5,6], [7,8,9]]
[[1,2,3], [4,5,6], [7,8,9]]
map
applies a function, f, to each element of a list, xs
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : map f xs
filter
selects elements from a list, xs, that satisfy a predicate, pred
filter :: (a -> Bool) -> [a] -> [a]
filter pred [] = []
filter pred (x:xs)
| pred x = x : filter pred xs
| otherwise = filter pred xs
Many list functions follow this pattern:
foo [] = v
foo (x:xs) = x # foo xs
foo [] = v
foo (x:xs) = x # foo xs
sum [] = 0
sum (x:xs) = x + sum xs
foo [] = v
foo (x:xs) = x # foo xs
sum [] = 0
sum (x:xs) = x + sum xs
product [] = 1
product (x:xs) = x * product xs
foo [] = v
foo (x:xs) = x # foo xs
sum [] = 0
sum (x:xs) = x + sum xs
product [] = 1
product (x:xs) = x * product xs
and [] = True
and (x:xs) = x && and xs
foo [] = v
foo (x:xs) = x # foo xs
sum [] = 0
sum (x:xs) = x + sum xs
product [] = 1
product (x:xs) = x * product xs
and [] = True
and (x:xs) = x && and xs
or [] = False
or (x:xs) = x || or xs
foo [] = v
foo (x:xs) = x # foo xs
sum [] = 0 -- v
sum (x:xs) = x + sum xs -- x # foo xs
product [] = 1 -- v
product (x:xs) = x * product xs -- x # foo xs
and [] = True -- v
and (x:xs) = x && and xs -- x # foo xs
or [] = False -- v
or (x:xs) = x || or xs -- x # foo xs
foldr
abstracts this pattern:
sum xs = foldr (+) 0 xs
product xs = foldr (*) 1 xs
and xs = foldr (&&) True xs
or xs = foldr (||) False xs
sum xs = foldr (+) 0 xs
product xs = foldr (*) 1 xs
and xs = foldr (&&) True xs
or xs = foldr (||) False xs
Try to write foldr’’
Step 1: Define the type
What type does (+), (*), (&&), and (||) have?
(+) :: Num a => a -> a -> a
(*) :: Num a => a -> a -> a
(&&) :: Bool -> Bool -> Bool
(||) :: Bool -> Bool -> Bool
It appears we need a function with type a -> a -> a
Step 1: Define the type
What type does the base case have?
It is the same type as the combining function (e.g. (+), (*), (&&), or (||)
Step 1: Define the type
Lastly, what type are all these functions recursing over?
A list of the types the combining function takes
Step 1: Define the type
Thus, our type is:
foldr'' :: (a -> a -> a) -> a -> [a] -> a
Step 1: Define the type
Thus, our type is:
foldr'' :: (a -> a -> a) -> a -> [a] -> a
( # ) v xs
foo [] = v
foo (x:xs) = x # foo xs
Step 2: Enumerate the cases
foldr'' f v [] = ...
foldr'' f v (x:xs) = ...
Step 3: Define the easy cases
foldr'' f v [] = v
foldr'' f v (x:xs) = ...
Step 4: Define the other cases
foldr'' f v [] = v
foldr'' f v (x:xs) = x `f` foldr'' f v xs
Step 5: Generalize and simplify
Before we do this let’s see how foldr''
works step by step
foldr'' :: (a -> a -> a) -> a -> [a] -> a
foldr'' f v [] = v
foldr'' f v (x:xs) = x `f` foldr'' f v xs
foldr'' (+) 0 [1,2,3]
| { apply foldr'' }
1 + foldr (+) 0 [2,3]
| { apply foldr'' }
1 + (2 + foldr'' (+) 0 [3])
| { apply foldr'' }
1 + (2 + (3 + foldr'' (+) 0 []))
| { apply foldr'' }
1 + (2 + (3 + 0))
| { apply + }
6
foldr'' :: (a -> a -> a) -> a -> [a] -> a
foldr'' f v [] = v
foldr'' f v (x:xs) = x `f` foldr'' f v xs
foldr'' (*) 1 [1,2,3]
| { apply foldr'' }
1 * foldr'' (*) 1 [2,3]
| { apply foldr'' }
1 * (2 * foldr'' (*) 1 [3])
| { apply foldr'' }
1 * (2 * (3 * foldr'' (*) 1 []))
| { apply foldr'' }
1 * (2 * (3 * 1))
| { apply * }
6
foldr'' :: (a -> a -> a) -> a -> [a] -> a
foldr'' f v [] = v
foldr'' f v (x:xs) = x `f` foldr'' f v xs
foldr'' (&&) True [True, False, True]
| { apply foldr'' }
True && foldr'' (&&) True [False, True]
| { apply foldr'' }
True && (False && foldr'' (&&) True [True])
| { apply foldr'' }
True && (False && (True && foldr'' (&&) True []))
| { apply foldr'' }
True && (False && (True && True))
| { apply && }
False
foldr'' :: (a -> a -> a) -> a -> [a] -> a
foldr'' f v [] = v
foldr'' f v (x:xs) = x `f` foldr'' f v xs
foldr'' (||) False [True, False, True]
| { apply foldr'' }
True || foldr'' (||) False [False, True]
| { apply foldr'' }
True || (False || foldr'' (||) False [True])
| { apply foldr'' }
True || (False || (True || foldr'' (||) False []))
| { apply foldr'' }
True || (False || (True || False))
| { apply || }
True
Can you write map'
in terms of foldr''
?
Try it!
foldr'' :: (a -> a -> a) -> a -> [a] -> a
foldr'' f v [] = v
foldr'' f v (x:xs) = x `f` foldr'' f v xs
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr'' ... ... ...
This doesn’t work, why?
foldr'' :: (a -> a -> a) -> a -> [a] -> a
foldr'' f v [] = v
foldr'' f v (x:xs) = x `f` foldr'' f v xs
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr'' (\x rest -> f x : rest) [] xs
x :: a
rest :: [b]
foldr'' :: (a -> a -> a) -> a -> [a] -> a
Our foldr''
function’s type is not general enough!
Step 5: Generalize and simplify
foldr'' :: (a -> b -> b) -> b -> [a] -> b
This type allows a and b to be the same if desired or different
Now we can write map'
in terms of foldr''
Step 6: Test the function
> foldr'' (+) 0 [1,2,3]
6
> foldr'' (*) 1 [1,2,3]
6
> foldr'' (&&) True [True, False, True]
False
> foldr'' (||) False [True, False, True]
True
Try to write filter’ in terms of foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = x `f` foldr f v xs
filter :: (a -> Bool) -> [a] -> [a]
filter pred [] = []
filter pred (x:xs)
| pred x = x : filter pred xs
| otherwise = filter pred xs
filter' :: (a -> Bool) -> [a] -> [a]
filter' pred xs = foldr ... ... ...
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = x `f` foldr f v xs
filter :: (a -> Bool) -> [a] -> [a]
filter pred [] = []
filter pred (x:xs)
| pred x = x : filter pred xs
| otherwise = filter pred xs
filter' :: (a -> Bool) -> [a] -> [a]
filter' pred xs = foldr go [] xs
where
go x rest
| pred x = x : rest
| otherwise = rest
What is the first argument to the function give to foldr represent?
The head of the list
What is the second argument represent?
The recursive call to the tail
Just look at the definition of foldr
:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = x `f` foldr f v xs
Can you write map
in terms of filter
or vice versa?
Try it!
You cannot!
This means map
and filter
are on the same level of power.
However, you can write map
and filter
with foldr
so we say that foldr
is more powerful
length :: [a] -> Int
length xs = foldr (\x rest -> 1 + rest) 0 xs
length :: [a] -> Int
length xs = foldr (\x rest -> 1 + rest) 0 xs
length [1,2,3]
| { apply foldr }
1 + foldr (\x rest -> 1 + rest) 0 [2,3]
| { apply foldr }
1 + (1 + foldr (\x rest -> 1 + rest) 0 [3])
| { apply foldr }
1 + (1 + (1 + foldr (\x rest -> 1 + rest) 0 []))
| { apply foldr }
1 + (1 + (1 + 0))
| { apply + }
3
Another perspective
foldr (\x rest -> 1 + rest) 0 [1,2,3]
1 : (2 : (3 : [])) becomes
1 + (1 + (1 + 0))
foldr essentially replaces each cons (:) with the combining function f
and the empty list with the base case v
foldr (+) 0 [1,2,3]
1 : (2 : (3 : [])) becomes
1 + (2 + (3 + 0))
foldr (*) 1 [1,2,3]
1 : (2 : (3 : [])) becomes
1 * (2 * (3 * 1))
foldr (&&) True [True, False, True]
True : (False : (True : [])) becomes
True && (False && (True && True))
foldr (||) False [True, False, True]
True : (False : (True : [])) becomes
True || (False || (True || False))
Try to write reverse'
in terms of foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = x `f` foldr f v xs
reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
reverse' :: [a] -> [a]
reverse' xs = foldr ... ... ...
Try to write reverse'
in terms of foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = x `f` foldr f v xs
reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
reverse' :: [a] -> [a]
reverse' xs = foldr go [] xs
where
go x rest = rest ++ [x]