Haskell Higher-Order Functions Guide — Map, Filter, Fold, and Function Composition
In this tutorial, you will learn about Haskell Higher. We cover key concepts, practical examples, and best practices to help you master this topic.
Haskell higher-order functions accept or return functions -- map transforms lists, filter selects elements, foldl/foldr reduce lists to values, and composition (.) and application ($) enable concise point-free programming without explicit arguments.
Map
-- Apply function to each element
map (*2) [1,2,3,4,5] -- [2,4,6,8,10]
map toUpper "hello" -- "HELLO"
map (++ "!") ["hi","bye"] -- ["hi!","bye!"]
-- With anonymous function
map (\x -> x ^ 2) [1..5] -- [1,4,9,16,25]
Filter
-- Select elements matching predicate
filter even [1..10] -- [2,4,6,8,10]
filter (>5) [1..10] -- [6,7,8,9,10]
filter ('l' ==) "hello" -- "ll"
filter ("Good" `isPrefixOf`)
["Good","Bad","Goodbye"] -- ["Good","Goodbye"]
Fold (Reduce)
-- foldl: left fold (process left to right)
foldl (+) 0 [1..5] -- 15
foldl (++) "" ["a","b","c"] -- "abc"
-- foldr: right fold (process right to left)
foldr (+) 0 [1..5] -- 15
foldr (:) [] [1,2,3] -- [1,2,3] (identity)
-- foldl1/foldr1: use first element as initial
foldl1 (+) [1..5] -- 15
maximum [3,1,4,1,5] -- 5 (implemented as foldl1 max)
Function Composition
-- Composition: (f . g) x = f (g x)
-- Apply g, then f
oddSquares = length . filter odd . map (^2)
oddSquares [1..10] -- 5 (squares of 1..10, count odds)
-- Application: ($) applies function to argument
-- ($) is right-associative, allowing fewer parentheses
sum (filter (>5) (map (*2) [1..10]))
-- same as:
sum $ filter (>5) $ map (*2) [1..10]
-- Point-free style
process = sum . filter (>5) . map (*2)
process [1..10] -- 126
Partial Application
-- All functions are curried (take one argument at a time)
add :: Int -> Int -> Int
add x y = x + y
addFive = add 5
addFive 10 -- 15
-- Using sections
double = (*2)
double 5 -- 10
isVowel = (`elem` "aeiou")
isVowel 'a' -- True
Common Mistakes
1. Overusing point-free style
Point-free can become unreadable: f = (sum .) . map is hard to parse. Use named arguments for complex logic.
2. Confusing foldl and foldr
foldl can stack overflow on large lists due to thunks. Use foldl' (strict) or foldr for large lists.
3. Not using sections
Instead of \x -> x + 1, use (+1). Instead of \x -> 2 ^ x, use (2^).
Practice Questions
1. What does map (+1) [1,2,3] return?
[2,3,4]. map applies the function to each element.
2. What is the difference between foldl and foldr? foldl associates left, foldr right. foldr works on infinite lists. foldl needs the full list.
3. What does (.) do?
Composes two functions: (f . g) x = f (g x). Reads as "f after g".
FAQ
{{< faq question="What is currying?" >}} Every Haskell function takes one argument and returns one value. Multi-argument functions are actually chains of single-argument functions. {{< /faq >}}
{{< faq question="When should I use foldl' over foldl?" >}} Always use foldl' (from Data.List) instead of foldl. foldl accumulates thunks that can overflow the stack. foldl' is strict. {{< /faq >}}
{{< faq question="What is point-free style?" >}}
Defining functions without explicitly naming arguments: Process = sum . filter (>5) . map (*2) instead of process xs = sum (filter (>5) (map (*2) xs)).
{{< /faq >}}
What's Next
Now learn about functors.
| Topic | Description | Link |
|---|---|---|
| Functors | Mapping over contexts with fmap | {{< ref "09-functors" >}} |
| Applicatives | Applicative functors | {{< ref "10-applicatives" >}} |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro