map, filter, and foldr

Contents
map and filter
foldr
foldl and foldl1
Which one

map and filter

Two of the three standard traversals transform a list without changing its length or keeping only some elements:

map (*2) [1,2,3] -- [2,4,6] filter even [1..10] -- [2,4,6,8,10] map show [1,2] ++ map show [3] -- ["1","2","3"]

Both take a function and a list, which is exactly what partial application from lesson 8 makes cheap. Chained, they replace most loops you would write in another language.

foldr

A fold replaces the constructors of a list with a function and a starting value. foldr works from the right, which is the same direction as the cons chain:

foldr (+) 0 [1,2,3] -- 6 foldr (:) [] [1,2,3] -- [1,2,3] foldr (\x acc -> x : acc) [] [1,2] -- [1,2]

The substitution view is exact: foldr f z [a,b,c] equals f a (f b (f c z)). Once you see that, every fold-based function is readable.

foldl and foldl1

foldl works from the left and carries an accumulator, which matches the loop you would write by hand:

foldl (+) 0 [1,2,3] -- 6 foldl (flip (++)) [] ["a","b"] -- ["b","a"] foldl1 (+) [1,2,3] -- 6, first element is the start

foldl1 and foldr1 drop the starting value and take the first or last element instead. They crash on an empty list, so they are partial functions.

Which one

foldr can stop early on an infinite list and works well when the combining function is lazy in its second argument. foldl cannot, and it builds a huge thunk, which is why the strict version foldl' exists:

-- works on an infinite list take 3 (foldr (\x acc -> x : acc) [] [1..]) -- strict left fold, the safe default for sums foldl' (+) 0 [1..1000000]

Use foldr to build a list or to short-circuit, and foldl' to accumulate a number. Plain foldl is almost always a mistake, and lesson 41 shows exactly why.

Next: Lambdas and composition

Article author: Arthur Isaev

Related articles
Haskell
Haskell course: zero to hero in 50 lessons
What is Haskell and why it matters
Install the toolchain with GHCup
GHCi: the interactive shell
Your first program
Values with let and where
Types and type signatures
Functions and application
Currying and partial application
Operators, sections, and fixity
if, guards, and pattern guards
case expressions
Pattern matching
Tuples
Lists
Ranges and enumeration
List comprehensions
Recursion replaces loops
map, filter, and foldr
Lambdas and composition
Type classes
Parametric polymorphism
Maybe and null safety
Either and recoverable errors
data: product types
data: sum types
Records
newtype and type synonyms
Recursive data: binary trees
Functor
Applicative
Monad
do notation
IO and purity
Files and IO
Modules and imports
Cabal projects
Dependencies
Testing with HUnit
Property testing with QuickCheck
Laziness and infinite lists
Strictness and space leaks
Folds
Monoids
Traversable
Monad transformers
Errors and exceptions
Concurrency
Parsing with Megaparsec
Capstone: a todo CLI
Hero roadmap: the whole course on one page

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: