Lecture 11 Higher Order Functions

Joseph Haugh

University of New Mexico

Review

  • What is a first class function?

  • What is an operator section?

  • What will the following code evaluate to?

    (*2) ((+1) 3)
  • 8

Review

  • What is currying?

  • What is the type of the take function?

  • take :: Int -> [a] -> [a]
  • What is the type of the following expression?

    take 10
  • [a] -> [a]

Currying

Implicitly curried:

add :: Int -> Int -> Int
add x y = x + y

Explicitly curried:

add :: Int -> (Int -> Int)
add = \x -> (\y -> x + y)

Currying

Implicitly curried:

add :: Int -> Int -> Int
add x y = x + y

take :: Int -> [a] -> [a]
take n xs = ...

Explicitly curried:

add :: Int -> (Int -> Int)
add = \x -> (\y -> x + y)

take :: Int -> ([a] -> [a])
take = \n -> (\xs -> ...)

Currying

Implicitly curried:

add :: Int -> Int -> Int
add x y = x + y

take :: Int -> [a] -> [a]
take n xs = ...

foo ::
  (a -> b) -> (a, a) -> (b, b)
foo f (x, y) = ...

Explicitly curried:

add :: Int -> (Int -> Int)
add = \x -> (\y -> x + y)

take :: Int -> ([a] -> [a])
take = \n -> (\xs -> ...)

foo ::
  (a -> b) -> ((a, a) -> (b, b))
foo = \f -> (\(x, y) -> ...)

Currying

Implicitly curried:

add :: Int -> Int -> Int
add x y = x + y

take :: Int -> [a] -> [a]
take n xs = ...

foo ::
  (a -> b) -> (a, a) -> (b, b)
foo f (x, y) = ...

hyper :: Int -> Integer ->
         Integer -> Integer
hyper x m n = ...

Explicitly curried:

add :: Int -> (Int -> Int)
add = \x -> (\y -> x + y)

take :: Int -> ([a] -> [a])
take = \n -> (\xs -> ...)

foo ::
  (a -> b) -> ((a, a) -> (b, b))
foo = \f -> (\(x, y) -> ...)

hyper :: Int -> (Integer ->
         (Integer -> Integer))
hyper = \x -> (\m -> (\n -> ...))

Currying

All of the previous examples are equivalent, since Haskell functions are curried by default.

Understanding this is crucial to understanding how to use higher order functions.

Higher Order Function: Twice

twice :: (a -> a) -> a -> a
twice f x = f (f x)

> twice (*3) 3

Higher Order Function: Twice

twice :: (a -> a) -> a -> a
twice f x = f (f x)

> twice (*3) 3
27

> twice (take 2) [1..10]

Higher Order Function: Twice

twice :: (a -> a) -> a -> a
twice f x = f (f x)

> twice (*3) 3
27

> twice (take 2) [1..10]
[1,2]

> twice (twice (*3)) 3

Higher Order Function: Twice

twice :: (a -> a) -> a -> a
twice f x = f (f x)

> twice (*3) 3
27

> twice (take 2) [1..10]
[1,2]

> twice (twice (*3)) 3
243

Higher Order Function: Twice

twice :: (a -> a) -> a -> a
twice f x = f (f x)

twice (twice (*3)) 3
| { applying outer twice }
twice (*3) (twice (*3) 3)
| { applying outer twice }
(*3) ((*3) (twice (*3) 3))
| { applying inner twice }
(*3) ((*3) ((*3) ((*3) 3)))
| { applying (*3)s }
243

Higher Order Function: Map

map (+1) [1,2,3]

Higher Order Function: Map

map (+1) [1,2,3]
[2,3,4]

map (*2) [1,2,3]

Higher Order Function: Map

map (+1) [1,2,3]
[2,3,4]

map (*2) [1,2,3]
[2,4,6]

map (take 2) ["hello", "world", "goodbye"]

Higher Order Function: Map

map (+1) [1,2,3]
[2,3,4]

map (*2) [1,2,3]
[2,4,6]

map (take 2) ["hello", "world", "goodbye"]
["he", "wo", "go"]

What does map do?

What is map’s type?

Higher Order Function: Map

Map applies a unary function to each element of a list.

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

Exercise: Map

Try to implement map'.

map' :: (a -> b) -> [a] -> [b]

Exercise: Map

Step 1: Define the type, done. Step 2: Enumerate the cases:

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

Exercise: Map

Step 3: Define the simple cases:

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

Exercise: Map

Step 4: Define the other cases:

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

Exercise: Map

Step 5: Generalize and simplify:

Can you do it with a list comprehension?

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

Exercise: Map

Step 6: Test your function:

> map' (+1) [1,2,3]
[2,3,4]

> map' (*2) [1,2,3]
[2,4,6]

> map' (take 2) ["hello", "world", "goodbye"]
["he", "wo", "go"]

Map Map

What does this expression evaluate to?

map (map (+1)) [[1,2,3], [4,5]]

Map Map

What does this expression evaluate to?

map (map (+1)) [[1,2,3], [4,5]]
| { applying outer map }
[map (+1) [1,2,3], map (+1) [4,5]]
| { applying inner maps }
[[2,3,4], [5,6]]

Higher Order Function: Filter

> filter even [1..10]
[2,4,6,8,10]

> filter (>5) [1..10]
[6,7,8,9,10]

> filter (== 'a') "banana"
"aaa"

What does filter do?

What is filter’s type?

Higher Order Function: Filter

Filter selects elements from a list that satisfy a predicate.

filter :: (a -> Bool) -> [a] -> [a]

Higher Order Function: Filter

filter :: (a -> Bool) -> [a] -> [a]
filter pred xs = [x | x <- xs, pred x]

How can we define filter using direct recursion?

Higher Order Function: Filter

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

Exercise: sumOddCubes

Define a function sumOddCubes that takes a list of integers and returns the sum of the cubes of the odd numbers in the list. You must use map and filter and no list comprehensions or explicit recursion.

sumOddCubes :: [Int] -> Int

Exercise: sumOddCubes

sumOddCubes :: [Int] -> Int
sumOddCubes xs = sum (map (^3) (filter odd xs))

What if you did do it with list comprehensions?

sumOddCubes :: [Int] -> Int
sumOddCubes xs = sum [x^3 | x <- xs, odd x]

Notice the similarities, the expression on the left hand side of the | is the map expression and the guard expression is the filter expression.

What about with explicit recursion?

Exercise: sumOddCubes

sumOddCubes :: [Int] -> Int
sumOddCubes [] = 0
sumOddCubes (x:xs)
    | odd x     = x^3 + sumOddCubes xs
    | otherwise = sumOddCubes xs

Higher Order Function: All

> all even [2,4,6]
True

> all even [2,4,5]
False

> all (\ls -> length ls > 2) [[1,2,3], [4,5,6]]
True

What does all do?

What is all’s type?

Higher Order Function: All

all checks if all elements of a list satisfy a predicate.

all :: (a -> Bool) -> [a] -> Bool
all pred xs = and (map pred xs)

Higher Order Function: Any

> any even [2,4,6]
True

> any even [2,4,5]
True

> any (\ls -> length ls > 2) [[1,2], [3,4]]
False

What does any do?

What is any’s type?

Higher Order Function: Any

any checks if any elements of a list satisfy a predicate.

any :: (a -> Bool) -> [a] -> Bool
any pred xs = or (map pred xs)

Higher Order Function: TakeWhile

> takeWhile even [2,4,6,3,4,5,6]
[2,4,6]

> takeWhile (\x -> x < 5) [1,3,5,7,2,4,6]
[1,3]

What does takeWhile do?

What is takeWhile’s type?

Higher Order Function: TakeWhile

takeWhile takes elements from a list while a predicate is true.

takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile pred []     = []
takeWhile pred (x:xs)
    | pred x    = x : takeWhile pred xs
    | otherwise = []

Higher Order Function: DropWhile

> dropWhile even [2,4,6,3,4,5,6]
[3,4,5,6]

> dropWhile (\x -> x < 5) [1,3,5,7,2,4,6]
[5,7,2,4,6]

What does dropWhile do?

What is dropWhile’s type?

Exercise: DropWhile

Try to implement dropWhile'.

dropWhile' :: (a -> Bool) -> [a] -> [a]

Exercise: DropWhile

Try to implement dropWhile'.

dropWhile' :: (a -> Bool) -> [a] -> [a]
dropWhile' pred []     = []
dropWhile' pred (x:xs)
    | pred x    = dropWhile' pred xs
    | otherwise = x:xs