0% found this document useful (0 votes)
6 views34 pages

3 1 HOFunc

The document covers higher-order functions in Haskell, including currying, partial application, and common higher-order functions like map, filter, and fold. It explains concepts such as function composition, lambdas, and point-free style, providing examples and type signatures for clarity. The lecture emphasizes the power and flexibility of higher-order functions in programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views34 pages

3 1 HOFunc

The document covers higher-order functions in Haskell, including currying, partial application, and common higher-order functions like map, filter, and fold. It explains concepts such as function composition, lambdas, and point-free style, providing examples and type signatures for clarity. The lecture emphasizes the power and flexibility of higher-order functions in programming.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 34

CMPT383 Comparative Programming Languages

Lecture 3-1: Higher-Order Functions

Yuepeng Wang
Spring 2025

1
Overview
• Currying

• Higher-order functions

• Common higher-order functions

• Lambdas

• Function composition

• Point-free style

2
Currying
• Let us forget the constant values for a moment.
Claim: every function in Haskell officially takes only one parameter

• Wait a second… we have seen so many functions with more than one
parameter. How can the claim be true?

• Because of currying

• Currying transforms a function that takes multiple parameters into a


sequence of functions where each takes a single parameter

• Named after mathematician and logician Haskell Curry

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

• What’s the meaning of the type signature?


• The add function takes two Ints and returns an Int

• Fact: the arrow -> in the type signature is always right-associative


• Int -> Int -> Int is the same as Int -> (Int -> Int)

• What’s the meaning of the type signature, really?


• The add function takes an Int and returns a function
• That returned function takes an Int and returns an Int

4
Partially Applied Functions
• If a function is called with fewer parameters than specified, we get a
partially applied function, which takes as many parameters as we left out

• Using partial application can create functions on the fly, and we can pass
them to other functions

• Example
addOne :: Int -> Int
addOne x = add 1 x

• Using partial application


addOne :: Int -> Int
addOne = add 1

5
Sections
• Infix operators can be partially applied using sections

• A section on infix operators is essentially a partially applied function

• To section an infix operator, surround it with parentheses and supply a


parameter on only one side

• Example: right section


dividedByTen :: Float -> Float
dividedByTen = (/10)

6
Sections
• Example: left section
tenDividedBy :: Float -> Float
tenDividedBy = (10/)

• Exception: the - operator cannot do a right section

• It will be interpreted as the unary negative, e.g. (-1)

• You need to use (subtract 1)

7
Higher-Order Functions
• Haskell functions can take functions as parameters and return functions
as return values

• A higher-order function is a function that does either of those things

• Higher-order functions are really powerful for programming

• C has function pointers

8
Example: applyTwice
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)

• The first parameter f is a function that takes a parameter and returns a


value of the same type

• The parenthesis in the type signature is mandatory

• The second parameter x is of type a

• The return value has type a

• The function applies f twice to x

9
Example: applyTwice
• Execution examples
ghci> applyTwice (+3) 10
16
ghci> applyTwice (++ "a") "b"
"baa"
ghci> applyTwice ("a" ++) "b"
"aab"
ghci> applyTwice (3:) [1, 2]
[3,3,1,2]

10
Common Function: zipWith
• zipWith (from Prelude) takes a function and two lists as parameters, and
then joins the two lists by applying the function between corresponding
elements

• Example: zipWith (+) [1, 2] [3, 4, 5] = [4, 6]

• What is the type of zipWith?

zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]


zipWith' _ _ [] = []
zipWith' _ [] _ = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys

11
Common Function: zipWith
• What’s the result of zipWith max [1, 3] [2, 4, 5]?

ghci> zipWith max [1, 3] [2, 4, 5]


[2,4]

• zipWith (zipWith (*)) [[1, 2], [3, 4]] [[5, 6], [7, 8]]

ghci> zipWith (zipWith (*)) [[1, 2], [3, 4]] [[5, 6], [7, 8]]
[[5,12],[21,32]]

12
Common Function: flip
• flip (from Prelude) takes a function with two parameters and returns a
function with those two parameters swapped

• Example: flip (-) 3 5 = 2

• What is the type of flip?

flip' :: (a -> b -> c) -> (b -> a -> c) -- optional second parathesis


flip' f x y = f y x

13
Common Function: filter
• filter (from Prelude) takes a predicate (with one parameter) and a list, and
returns a list of elements that satisfy the predicate

• A predicate is a boolean function

• Example: filter even [1, 2, 3, 4] = [2, 4]

• What is the type of filter?


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

14
Common Function: map
• map (from Prelude) takes a function f (with one parameter) and a list L,
and returns a list with f applied to each element in L

• Example: map (+1) [1, 2, 3] = [2, 3, 4]

• What is the type of map?

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


map' _ [] = []
map' f (x:xs) = f x : map' f xs

15
Common Function: map
map' :: (a -> b) -> [a] -> [b]
map' _ [] = []
map' f (x:xs) = f x : map' f xs

• What if f takes more than one parameter, e.g. map (/) [0,1,2,3]?

• map will return a list of partially applied functions because of currying


ghci> let fs = map (/) [0,1,2,3]
ghci> (fs !! 2) 10
0.2

ghci> let fs = map (flip (/)) [0,1,2,3]


ghci> (fs !! 2) 10
5.0

16
Lambdas
• Lambdas are anonymous functions

• Typically used when we need a function only once

• Lambdas are expressions

• Declare a lambda: \ parameters -> function body

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


[4,5,6]
ghci> map (\x -> x + 3) [1, 2, 3]
[4,5,6]

17
Lambdas
• Lambdas can have multiple parameters
ghci> zipWith (\x y -> 10 * x + y) [1, 2, 3] [3, 2, 1]
[13, 22, 31]

• You can use pattern matching in lambdas, but you cannot define multiple
patterns for a parameter
ghci> map (\(x,y) -> x + y) [(1,2),(3,4)]
[3,7]

• You can use lambdas in lambdas


addThree :: Int -> Int -> Int -> Int
addThree = \x -> \y -> \z -> x + y + z

18
Folds
• Common patterns exist for recursive functions over lists

• Folds can be used to implement any function where you traverse a list
once, element by element, and then return something based on that

• A fold takes a binary function, a starting value (often called the


accumulator), and a list to fold up

• Lists can be folded up from the left or from the right

19
Folds
• What do folds do on lists, conceptually?

• The fold function calls the binary function, using the accumulator and
the first (or last) element of the list as parameters

• The resulting value is the new accumulator

• It calls the binary function again, using the new accumulator and the
next (or previous) element of the list, getting a new accumulator

• This repeats until the entire list is traversed and reduced down to a
single accumulator value

20
Common Function: foldl
• foldl (from Prelude) folds the list up from the left side
f
• How to evaluate foldl f x [1, 2, 3]?
f 3
• What is the type of foldl (for lists)?
f 2

• (a -> b -> a) -> a -> [b] -> a x 1

• Example: sum that computes the sum of a list of numbers

sum' :: Num a => [a] -> a


sum' xs = foldl (+) 0 xs

21
Common Function: foldr
• foldr (from Prelude) folds the list up from the right side
f
• How to evaluate foldr f x [1, 2, 3]?
1 f
• What is the type of foldr (for lists)? 2 f

• (b -> a -> a) -> a -> [b] -> a 3 x

• Example: sum that computes the sum of a list of numbers

sum' :: Num a => [a] -> a


sum' xs = foldr (+) 0 xs

22
foldl vs. foldr
• Difference between foldl and foldr?

• The accumulator takes the values from left vs. right

• The order of the parameters in the binary function is flipped

• Example: use folds to implement map

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


map' f xs = foldr (\x acc -> f x : acc) [] xs

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


map' f xs = foldl (\acc x -> acc ++ [f x]) [] xs

23
scanl and scanr
• The scanl and scanr functions are similar to foldl and foldr on lists, except
they report all the intermediate accumulator values in the form of a list
ghci> scanl (+) 0 [1,2,3,4]
[0,1,3,6,10]
ghci> scanr (+) 0 [1,2,3,4]
[10,9,7,4,0]

• Can be useful for debugging folds!

24
Function Applications
• Haskell has an infix operator $ called function application operator
($) :: (a -> b) -> a -> b
f $ x = f x

• What’s the difference between $ and normal function application?


$ operator Normal Application
Precedence lowest very high
Associativity right left

• Why is this useful?

25
Function Applications
• $ operators can save parentheses

• Example
ghci> sum (filter (> 10) (map (* 2) [1..10]))
80

• Using $ operators
ghci> sum $ filter (> 10) $ map (* 2) [1..10]
80

26
Function Applications
• $ treats function application just like another function

• Example
ghci> map ($ 3) [(1+), (2*), (^3), sqrt]
[4.0, 6.0, 27.0, 1.7320508075688772]

• Every function in the list gets applied to 3

27
Function Composition
• Function composition is defined like ( f ∘ g)(x) = f(g(x)) in math

• In Haskell, function composition is the same

• Haskell has an infix operator . for function composition (right-associative)


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

• The first parameter f is a function of type b -> c

• The second parameter g is a function of type a -> b

• It returns a function of type a -> c


28
Function Composition: Example
negateAbs :: [Int] -> [Int]
negateAbs xs = map (\x -> negate (abs x)) xs

• Use $ for function application


negateAbs' :: [Int] -> [Int]
negateAbs' xs = map (\x -> negate $ abs x) xs

• Use . for function composition


negateAbs'' :: [Int] -> [Int]
negateAbs'' xs = map (negate . abs) xs

29
Composition with Multi-Parameters
• If we want to use composition for functions with multiple parameters, we
usually need to partially apply them so that each function takes only one
parameter
sum (replicate 5 (max 1 2))

• This expression can be rewritten as follows


(sum . replicate 5) (max 1 2)

• It’s equivalent to
sum . replicate 5 $ max 1 2

• Note the precedence: normal application > . operator > $ operator

30
Composition with Multi-Parameters
• How to rewrite an expression with many parentheses using function
composition?
• First write the innermost function and its parameters
• Then put a $ before it
• Compose all the functions that come before by writing them without the
last parameter and putting dots between them

• Example
replicate 5 (sum (map (+1) (zipWith max [1, 2] [3, 4])))

replicate 5 . sum . map (+1) $ zipWith max [1, 2] [3, 4]

31
Point-Free Style
• Point-free style is a programming style where function definitions do not
identify the parameters (or “points”) on which they operate

• Example
sum' :: Num a => [a] -> a
sum' xs = foldl (+) 0 xs

• Point-free style
sum'' :: Num a => [a] -> a
sum'' = foldl (+) 0

• Note that we can omit xs on both sides because of currying

32
Point-Free Style
• Another example
foo :: [Int] -> [Int]
foo xs = replicate 5 (sum (filter (>0) (map (+1) xs)))

• Cannot omit xs directly (type error). Remove parentheses


foo' :: [Int] -> [Int]
foo' xs = replicate 5 . sum . filter (>0) . map (+1) $ xs

• Point-free style
foo'' :: [Int] -> [Int]
foo'' = replicate 5 . sum . filter (>0) . map (+1)

• Functions in point-free style may have many dots!

• If a function is too complex, writing it in point-free style can be less readable

33
Review of Application and Composition
• What is the result of f $ x?

• What is the result of (f . g) x?

• What is the type of the $ operator?

• What is the type of the . operator?


$ operator . operator Func App
Type (a -> b) -> a -> b (b -> c) -> (a -> b) -> a -> c -
Precedence lowest high very high
Associativity right right left

• f $ g x == f $ (g x) == f (g x) == (f . g) x == (f . g) $ x == f . g $ x
34

You might also like