3 1 HOFunc
3 1 HOFunc
Yuepeng Wang
Spring 2025
1
Overview
• Currying
• 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
3
Currying
add :: Int -> Int -> Int
add x y = x + y
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
5
Sections
• Infix operators can be partially applied using sections
6
Sections
• Example: left section
tenDividedBy :: Float -> Float
tenDividedBy = (10/)
7
Higher-Order Functions
• Haskell functions can take functions as parameters and return functions
as return values
8
Example: applyTwice
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f 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
11
Common Function: zipWith
• What’s the result of zipWith max [1, 3] [2, 4, 5]?
• 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
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
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
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]?
16
Lambdas
• Lambdas are anonymous functions
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]
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
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
• 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
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
22
foldl vs. foldr
• Difference between foldl and foldr?
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]
24
Function Applications
• Haskell has an infix operator $ called function application operator
($) :: (a -> b) -> a -> b
f $ x = f x
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]
27
Function Composition
• Function composition is defined like ( f ∘ g)(x) = f(g(x)) in math
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))
• It’s equivalent to
sum . replicate 5 $ max 1 2
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])))
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
32
Point-Free Style
• Another example
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)
33
Review of Application and Composition
• What is the result of f $ x?
• f $ g x == f $ (g x) == f (g x) == (f . g) x == (f . g) $ x == f . g $ x
34