Lecture 15 Compose

Joseph Haugh

University of New Mexico

Free Recall

  • Open a blank text file or grab a piece of paper
  • Think about everything you can remember from last lecture and beyond

Review

What is a better name for this function?

foo f xs = foldr (\x rest -> f x : rest) [] xs

map

Review

What type does this expression have?

map (foldr (:) []) ::

Review

map (foldr (:) []) :: [[a]] -> [[a]]

foldr              :: (a ->  b  ->  b) -> b -> [a] -> b
(:)                ::  a -> [a] -> [a]
                      a = a, b = [a]
foldr (:)          :: [a] -> [a] -> [a]
foldr (:) []       ::  [a] -> [a]
map                :: ( a  ->  b) -> [a] -> [b]
                      a = [a], b = [a]
map (foldr (:) []) :: [[a]] -> [[a]]

Review

What does this expression evaluate to?

add1 x = x + 1
div2 x = x `div` 2

div2 (add1 3) ===

Review

add1 x = x + 1
div2 x = x `div` 2

div2 (add1 3) === 2

One More Higher Order Function

So far we have seen many higher order functions:

map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : map f xs

One More Higher Order Function

So far we have seen many higher order functions:

filter :: (a -> Bool) -> [a] -> [a]
filter p [] = []
filter p (x:xs)
    | p x = x : filter p xs
    | otherwise = filter p xs

One More Higher Order Function

So far we have seen many higher order functions:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)

One More Higher Order Function

So far we have seen many higher order functions:

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f acc [] = acc
foldl f acc (x:xs) = foldl f (acc `f` x) xs

One More Higher Order Function

But we haven’t seen a function that both takes a function as an argument and returns a function as a result.

Why might such a function be useful?

Compose

Recall from the review:

add1 x = x + 1
div2 x = x `div` 2

div2 (add1 3)

Could we write a function which generalizes this pattern?

Compose

add1 x = x + 1
div2 x = x `div` 2

div2 (add1 3)

What type would such a function have?

It would need to take in two functions and return a function.

The inner function, add1, must return the same type as the input to the outer function, div2.

Then the whole function would take the input type of the inner function and return the output type of the outer function.

Compose

(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)

Notice that g is applied first, thus, the order goes from right to left.

Now we could rewrite the review question as:

add1 x = x + 1
div2 x = x `div` 2

(div2 . add1) 3

Simplifying Nesting With Compose

We can simplify nesting with compose:

odd n = not (even n)

twice f x = f (f x)

sumSqrEven ns = sum (map (^2) (filter even ns))

Simplifying Nesting With Compose

We can simplify nesting with compose:

odd = not . even

twice f x = f (f x)

sumSqrEven ns = sum (map (^2) (filter even ns))

Simplifying Nesting With Compose

We can simplify nesting with compose:

odd = not . even

twice f = f . f

sumSqrEven ns = sum (map (^2) (filter even ns))

Simplifying Nesting With Compose

We can simplify nesting with compose:

odd = not . even

twice f = f . f

sumSqrEven = sum . map (^2) . filter even

Simplifying Nesting With Compose

What happened to the parameters?

odd = not . even

twice f = f . f

sumSqrEven = sum . map (^2) . filter even

This conversion is called eta reduction.

You have probably seen HLS yell at you for it many times.

So what does it mean?

Eta Reduction

In general eta reduction is the turning an expression of the form:

\x -> f x

Into

f

Eta Reduction Example

For example:

add1All :: [Int] -> [Int]
add1All xs = map (+1) xs

Can be rewritten as:

add1All :: [Int] -> [Int]
add1All = map (+1)

Eta Reduction

You might be asking yourself well doesn’t that mess up the type of the function?

Take a look at the above example again:

add1All :: [Int] -> [Int]
add1All xs = map (+1) xs

What is the type of add1All xs? [Int]

What is the type of map (+1) xs? [Int]

Eta Reduction

add1All :: [Int] -> [Int]
add1All = map (+1)

What is the type of add1All? [Int] -> [Int]

What is the type of map (+1)? [Int] -> [Int]

The types still match!

Identity Elements

Recall identity elements of functions:

x + 0 = x
0 + x = x

x * 1 = x
1 * x = x

Does (.) have an identity element?

Exercise: Identity Elements

Attempt to define a function, foo, that when given to (.) will exhibit the following behavior:

foo . g = g
f . foo = f

Exercise: Identity Elements

Attempt to define a function, foo, that when given to (.) will exhibit the following behavior:

foo . g = g
f . foo = f

foo :: a -> a
foo x = x

((+1) . foo) 3 === 4
(foo . (+1)) 3 === 4

This is the builtin function id.

Exercise: Compose List

Define a function, composeList, which takes in a list of functions, a -> a, and returns a single function, a -> a, which composes the functions in the list from right to left. Define it with explicit recursion:

composeList :: [a -> a] -> a -> a

Exercise: Compose List

Define a function, composeList, which takes in a list of functions, a -> a, and returns a single function, a -> a, which composes the functions in the list from right to left. Define it with explicit recursion:

composeList :: [a -> a] -> a -> a
composeList []     = \x -> x
composeList (f:fs) = f . composeList fs

Exercise: Compose List

Now define it with foldr:

composeList :: [a -> a] -> a -> a

Exercise: Compose List

Now define it with foldr:

composeList :: [a -> a] -> a -> a
composeList = foldr (.) id

Exercise: IterateN

Define a function, iterateN, which takes in a function, f a -> a, and an integer, n, and composes the function with itself n times. Define it with explicit recursion:

iterateN :: (a -> a) -> Int -> a -> a

Exercise: IterateN

Define a function, iterateN, which takes in a function, f a -> a, and an integer, n, and composes the function with itself n times. Define it with explicit recursion:

iterateN :: (a -> a) -> Int -> a -> a
iterateN f 0 = id
iterateN f n = f . iterateN f (n-1)

Exercise: IterateN

Now define it with foldr:

iterateN :: (a -> a) -> Int -> a -> a

Exercise: IterateN

Now define it with foldr:

iterateN :: (a -> a) -> Int -> a -> a
iterateN f n = foldr (\x rest -> f . rest) id [0..n]

Heron’s Method

Heron’s method is an iterative way to approximate the square root of a number. It is given by the following equation:

$x_{n+1} = \frac{1}{2} \left( x_n + \frac{S}{x_n} \right)$

Where x0 is the initial guess and xn + 1 is the next guess.

How can we write this in Haskell?

Exercise: Heron’s Method

First just translate this formula:

$x_{n+1} = \frac{1}{2} \left( x_n + \frac{S}{x_n} \right)$

Into Haskell, where the first argument is the number you are trying to find the square root of, S, and the second argument is the current guess, xn.

heron :: Double -> Double -> Double

Exercise: Heron’s Method

First just translate this formula:

$x_{n+1} = \frac{1}{2} \left( x_n + \frac{S}{x_n} \right)$

Into Haskell, where the first argument is the number you are trying to find the square root of, S, and the second argument is the current guess, xn.

heron :: Double -> Double -> Double
heron s x = (x + s / x) / 2

Exercise: Heron’s Method

Now how can we use this to iteratively improve our guess?

Hint: Use iterateN.

sqrt' :: Double -> Double

Exercise: Heron’s Method

Now how can we use this to iteratively improve our guess?

Hint: Use iterateN.

sqrt' :: Double -> Double
sqrt' s = iterateN (heron s) 10 s

Can we eta reduce this?

No! Why?

Because s appears in a place other than the last argument.

Eta Reduction Revisited

\x -> f x
f {as long as x is not used in f, in other words
   f has no free occurrences of x}