46
Higher Order Functions Chapter 11 of Thompson http://learnyouahaskell.com/higher-order-functions

Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Higher Order FunctionsChapter 11 of Thompson

http://learnyouahaskell.com/higher-order-functions

Page 2: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

A function that takes another function as input, or returns a function asoutput, is called a higher-order function.

Mastering higher order functions will make you a more productiveprogrammer.

Higher order functions

Page 3: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

A first example

We met the parametric polymorphic identity function:

id :: a -> aid x = x

Function types are just like any other type, so…

id :: (Int -> Char) -> (Int -> Char)

Page 4: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Reminder about the associativity of ->

(Int -> Char) -> (Int -> Char)

Could be written as

(Int -> Char) -> Int -> Char

But not as Int -> Char -> (Int -> Char)or Int -> Char -> Int -> Char

Page 5: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

You have already seen many functions in this course with functions asoutput – any function with more than one ‘input’!

f :: Int -> (String -> Char)

• Two inputs (an Int and a String), returning a Char ✓• One input (an Int), returning a function of type String -> Char ✓

The second point of view is called partial application.

Functions as output

Page 6: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

multiply :: Int -> Int -> Intmultiply x y = x * y

Page 7: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

multiply :: Int -> Int -> Intmultiply x y = x * y

multiply 2 3

26

3

Page 8: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

multiply :: Int -> (Int -> Int)(multiply x) y = x * y

(multiply 2) 3

26

3

Page 9: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

multiply :: Int -> Int -> Intmultiply x y = x * y

multiply 2 is a function Int -> Int that doubles its input!

2

Partial application

Page 10: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

So just asmultiply :: Int -> Int -> Int

is convenient shorthand formultiply :: Int -> (Int -> Int)

We havemultiply 2 3

as convenient shorthand for(multiply 2) 3

Page 11: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

In general:

f e1 e2 … ekt1 -> t2 -> ... tn -> t

are shorthands for:

((...((f e1) e2)...) ek)t1 -> (t2 -> (...(tn -> t)...))

i.e., function application is left-associative-> is right-associative

Page 12: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Functions as input

applyTwice:: (a -> a) -> a -> aapplyTwice f x = f (f x)

> applyTwice sqrt 162.0> applyTwice (+3) 1016

Page 13: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Functions as input

applyTwice:: (a -> a) -> a -> aapplyTwice f x = f (f x)

> applyTwice (++ " HAHA") "HEY""HEY HAHA HAHA”> applyTwice ("HAHA " ++) "HEY""HAHA HAHA HEY"> applyTwice (3:) [1][3,3,1]

Page 14: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Functions as input and output – Composition

Recall from the very first week that, given functionsf :: a -> b and g :: b -> c

We can define their composition, a functiong . f :: a -> c

So composition itself is a higher order function, with functions as both inputs and output.

Page 15: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Composition

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

We usually write (.) g f infix as g . f

> ((+1) . (*2)) 13> ((*2) . (+1)) 14

Page 16: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

We have built-in functionseven :: Int -> Boolnot :: Bool -> Bool

Assume we want to definemyOdd :: Int -> BoolmyOdd x = not (even x)

more succinctly:

myOdd’ = not . even

Page 17: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order
Page 18: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order
Page 19: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order
Page 20: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Fold (also called “reduce”) functions

Often we wish to traverse a list once, element by element, building up an output as we go.

This is called a fold.

There are ‘right folds’ and ‘left folds’; we will try to understand right folds before we look at the left.

These are the most useful higher order function and are worth understanding – they will make you a more productive programmer!

Page 21: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Folds

Often we wish to traverse a list once, element by element, building up an output as we go.

• Add each number in a list to get the sum of all the elements;• Multiply each number in a list to get the product of all the elements;• Increment a number by 1 for each element to get the list’s length;• Turn a list of strings into an acronym;• Map a function over a list element by element;• etc…

Page 22: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

mySum :: [Int] -> IntmySum list = case list of[] -> 0x:xs -> x + mySum xs

myLen :: [a] -> IntmyLen list = case list of[] -> 0x:xs -> 1 + myLen xs

myProd :: [Int] -> IntmyProd list = case list of[] -> 1x:xs -> x * myProd xs

acro :: [String] -> Stringacro list = case list of[] -> ""x:xs -> head x : acro xs

How are these definitions different? How are they the same?

Page 23: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

mySum :: [Int] -> IntmySum list = case list of[] -> 0x:xs -> x + mySum xs

myLen :: [a] -> IntmyLen list = case list of[] -> 0x:xs -> 1 + myLen xs

myProd :: [Int] -> IntmyProd list = case list of[] -> 1x:xs -> x * myProd xs

acro :: [String] -> Stringacro list = case list of[] -> ""x:xs -> head x : acro xs

The names are different – but Haskell doesn’t really care about that

Page 24: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

mySum :: [Int] -> IntmySum list = case list of[] -> 0x:xs -> x + mySum xs

myLen :: [a] -> IntmyLen list = case list of[] -> 0x:xs -> 1 + myLen xs

myProd :: [Int] -> IntmyProd list = case list of[] -> 1x:xs -> x * myProd xs

acro :: [String] -> Stringacro list = case list of[] -> ""x:xs -> head x : acro xs

The types are different – suggests folds are polymorphic

Page 25: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

mySum :: [Int] -> IntmySum list = case list of[] -> 0x:xs -> x + mySum xs

myLen :: [a] -> IntmyLen list = case list of[] -> 0x:xs -> 1 + myLen xs

myProd :: [Int] -> IntmyProd list = case list of[] -> 1x:xs -> x * myProd xs

acro :: [String] -> Stringacro list = case list of[] -> ""x:xs -> head x : acro xs

The base cases are different

Page 26: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

mySum :: [Int] -> IntmySum list = case list of[] -> 0x:xs -> x + mySum xs

myLen :: [a] -> IntmyLen list = case list of[] -> 0x:xs -> 1 + myLen xs

myProd :: [Int] -> IntmyProd list = case list of[] -> 1x:xs -> x * myProd xs

acro :: [String] -> Stringacro list = case list of[] -> ""x:xs -> head x : acro xs

The step cases are different – but only a little! – different recipes to combine the head with the recursive call on the tail

Page 27: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

mySum :: [Int] -> IntmySum list = case list of[] -> 0x:xs -> x + mySum xs

myLen :: [a] -> IntmyLen list = case list of[] -> 0x:xs -> 1 + myLen xs

myProd :: [Int] -> IntmyProd list = case list of[] -> 1x:xs -> x * myProd xs

acro :: [String] -> Stringacro list = case list of[] -> ""x:xs -> head x : acro xs

Everything else is the same!

Page 28: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Fold Right

This suggests that we could define a polymorphic higher order function that takes as input• a base case;• a recipe for combining the head with a recursive call on the tailand then does all of the rest of the work for us!

No more time-consuming error-prone recursions to write• Except for all the ones that don’t follow the format of the previous slide

Page 29: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order
Page 30: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

foldr

Page 31: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

foldr in action

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

foldr :: (Int -> Int -> Int) -> Int -> [Int] -> Int

mySum = foldr (+) 0

myProd = foldr (*) 1

Page 32: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

foldr in action

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

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

myLen = foldr (\x y -> y + 1) 0

Or, to avoid a warning:

myLen = foldr (\_ y -> y + 1) 0

Page 33: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

foldr in action

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

foldr :: (String -> String -> String) -> String -> [String] -> String

acro = foldr (\x y -> head x : y) ""

How might you write a ‘safe’ version of acro that ignores empty strings instead of crashing on them?

Page 34: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

foldr in action

foldr is just another function, and can be used as a helper anywhere:

myMaximum :: [Int] -> IntmyMaximum list = case list of[] -> error "No maximum of an empty list"x:xs -> foldr max x xs

Take some time to understand all the types here!

Page 35: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Defining foldr

myFoldr :: (a -> b -> b) -> b -> [a] -> bmyFoldr combine base list = case list of[] -> basex:xs -> combine x (myFoldr combine base xs)

Page 36: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Why is it fold right?

foldr (+) 0 [1,2,3]= 1 + foldr (+) 0 [2,3]= 1 + (2 + foldr (+) 0 [3])= 1 + (2 + (3 + foldr (+) 0 []))= 1 + (2 + (3 + 0))

So the combining operation associates to the right.• i.e. start with the base case, combine it with the rightmost element of list,

then continue until we reach the leftmost element of the list.

Page 37: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

foldr and the structure of lists

A list [1,2,3]is really 1 : (2 : (3 : []) )

foldr replaces [] with a base case and : with a combining function

e.g. 1 + (2 + (3 + 0) )

So folding right is very natural because lists themselves associate right

Page 38: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order
Page 39: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Left folds

mySuml :: [Int] -> IntmySuml = mySumAcc 0wheremySumAcc acc list = case list of[] -> accx:xs -> mySumAcc (acc + x) xs

In the above, mySumAcc has type Int -> [Int] -> Int

Page 40: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Left folds

mySuml [1,2,3]= mySumAcc 0 [1,2,3]= mySumAcc (0 + 1) [2,3]= mySumAcc ((0 + 1) + 2) [3]= mySumAcc (((0 + 1) + 2) + 3) []= (((0 + 1) + 2) + 3)

No difference for +, but not all combining operations are associative!

Page 41: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order
Page 42: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

It folds the list up from the left side

Page 43: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

Defining foldl

myFoldl :: (b -> a -> b) -> b -> [a] -> bmyFoldl combine acc list = case list of[] -> accx:xs -> myFoldl combine (combine acc x) xs

Compare to the final line of the right fold:

x:xs -> combine x (myFoldr combine base xs)

Page 44: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

foldl, folding left

foldl (+) 0 [1,2,3]= foldl (+) (0 + 1) [2,3]= foldl (+) ((0 + 1) + 2) [3]= foldl (+) (((0 + 1) + 2) + 3) []= (((0 + 1) + 2) + 3)

So the combining operation associates to the left.• i.e. start with the accumulator, combine it with the leftmost element of list,

then continue until we reach the empty list and return the accumulator.

Page 45: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

foldr versus foldl

An example where they give different answers:

> foldr (-) 0 [1,2,3]2

> foldl (-) 0 [1,2,3]-6

Because ((0 – 1) – 2) – 3 ≠ 1 – (2 – (3 – 0))

Page 46: Higher Order Functions · 2019. 7. 18. · A function that takes another function as input, or returns a function as output, is called a higher-order function. Mastering higher order

foldr versus foldl

More generally, if we think of lists as built up from the empty list by using cons repeatedly, then lists are constructed from the right.

Therefore foldr tends to follow the way lists are constructed.

e.g. foldr (:) [] :: [a] -> [a] is the identity!

foldl goes in the reverse direction from the list’s construction• What happens if you use (:) with foldl?