- How to recognise and write simple programs in a functional style.
- What a higher-order function is and why it is useful.
- How to use map, filter, and reduce/fold on lists.
- How to trace a small functional pipeline step by step.
Functional programming is a way of writing programs by building and applying functions. Instead of focusing on changing variables step by step, you describe transformations from inputs to outputs.
AQA expects you to have experience constructing simple programs in a functional programming language or in a language with functional features.
Functional programming languages include:
- Haskell
- Standard ML
- Scheme
- Lisp
Many multi-paradigm languages also support functional programming, including Python, F#, C#, Scala, Java 8 onwards, Delphi XE onwards, and VB.NET 2008 onwards.
Functional programming language
A functional programming language is a language designed to support programs built mainly from functions, often treating functions as values and encouraging expressions that produce results rather than commands that change state.
You do not need to use the same language as another student. The important thing is that you can read, write, and trace simple functional ideas such as passing a function into another function.
In functional programming, a function can often be treated like data: it can be stored in a variable, passed into another function, or returned as the result of a function.
First-class function
A first-class function is a function that can be used as a value: assigned to a name, passed as an argument, or returned from another function.
For example, a function called double could be passed into another function that applies it to every item in a list.
You will also often see short unnamed functions.
Anonymous function
An anonymous function, often called a lambda, is a function written without giving it a permanent name. It is useful when a small function is only needed once.
In pseudocode, you might see a lambda written like this:
x -> x * 2
This means “take x and return x * 2”.
A higher-order function is one of the most important ideas in this topic.
Higher-order function
A function is higher-order if it takes a function as an argument, returns a function as a result, or does both.
For example:
applyTwice(functionToApply, value)
could be higher-order because one of its inputs is itself a function.
Identifying higher-order functions
Decide which of these functions are higher-order:
square(x) returns x * x
applyTwice(f, x) returns f(f(x))
makeAdder(n) returns the function x -> x + n
square(x) takes an ordinary value and returns an ordinary value, so it is not higher-order.
applyTwice(f, x) takes f as an argument, and f is used like a function, so applyTwice is higher-order.
makeAdder(n) returns a new function, x -> x + n, so makeAdder is higher-order.
Why higher-order functions matter
Higher-order functions let you separate what operation should be done from how the data structure is processed.
The most common higher-order functions you need to use are map, filter, and reduce/fold. They are usually applied to lists.
The three standard operations are easiest to compare as a data flow.
![Diagram comparing map, filter and reduce/fold on the list [1, 2, 3, 4]](/_next/image?url=https%3A%2F%2Fassets.mathsgenie.co.uk%2Fnotes%2F83bd27ee-81ee-466c-b476-786f52efdee7.png&w=3840&q=75)
map applies a given function to each element of a list, returning a list of results.
map
map is a higher-order function that applies a supplied function to every element in a list and returns a new list containing the results.
For example, mapping x -> x * 2 over [1, 2, 3] gives [2, 4, 6].
The key point is that map keeps the same number of elements. Each item is transformed, but no items are removed.
Tracing map
A list stores temperatures in Celsius:
temperatures <- [0, 10, 20]
Use map with the function F=1.8C+32F = 1.8C + 32F=1.8C+32 to convert each temperature to Fahrenheit.
- Apply the function to the first value: 1.8×0+32=321.8 \times 0 + 32 = 321.8×0+32=32.
- Apply the same function to the second value: 1.8×10+32=501.8 \times 10 + 32 = 501.8×10+32=50.
- Apply the same function to the third value: 1.8×20+32=681.8 \times 20 + 32 = 681.8×20+32=68.
- Collect the results in the same order, giving
[32, 50, 68].
map keeps the shape
If the input list has five items, the output from map also has five items. The values may change, but the list length does not.
filter uses a condition to decide which elements should stay in a list.
Predicate
A predicate is a function that returns a Boolean value: either TRUE or FALSE.
filter
filter is a higher-order function that processes a data structure, usually a list, and returns a new data structure containing exactly the elements that satisfy a given condition.
For example, filtering [3, 8, 10, 15] with the predicate x -> x >= 10 gives [10, 15].
Tracing filter
A list stores file sizes in MiB:
sizes <- [12, 55, 8, 73, 30]
Use filter to keep only files larger than or equal to 50 MiB.
- Test
12 against the condition size >= 50; it is false, so remove it from the result.
- Test
55; it satisfies the condition, so keep it.
- Test
8; it does not satisfy the condition, so remove it.
- Test
73; it satisfies the condition, so keep it.
- Test
30; it does not satisfy the condition, so remove it.
- The filtered list is
[55, 73].
Confusing map and filter
map changes every item and keeps the list length the same. filter keeps or removes whole items and may make the list shorter.
reduce, also called fold, repeatedly applies a combining function to reduce a list of values to a single value.
Accumulator
An accumulator is a value that stores the result built up so far during a reduce/fold operation.
reduce or fold
reduce or fold is a higher-order function that reduces a list to a single value by repeatedly applying a combining function to the list values.
A fold normally needs:
- a starting accumulator value
- a combining function
- a list to process
For summing numbers, the starting accumulator is usually 0, because adding 0 does not change the total.
Tracing a fold
Fold the list [4, 7, 2] using the combining function accumulator + item, starting with accumulator 0.
- Start with accumulator
0, then combine with the first item: 0 + 4 gives accumulator 4.
- Combine accumulator
4 with the next item: 4 + 7 gives accumulator 11.
- Combine accumulator
11 with the final item: 11 + 2 gives accumulator 13.
- The list is exhausted, so the fold result is
13.
Fold order can matter
If the combining operation is not order-independent, such as subtraction or string concatenation, changing the fold direction or the item order can change the result.
A typical simple functional program uses a pipeline:
- filter to select the data you care about
- map to transform that data
- fold/reduce to combine the transformed data into one result
Pipeline
A pipeline is a sequence of operations where the output of one operation becomes the input to the next.
Suppose you have exam scores. You want to:
- keep only scores of 60 or more
- add 2 moderation marks to each remaining score
- calculate the total of the moderated scores
In language-neutral pseudocode:
scores <- [42, 76, 58, 91, 63]
passingScores <- filter(score -> score >= 60, scores)
moderatedScores <- map(score -> score + 2, passingScores)
total <- fold((accumulator, score) -> accumulator + score, 0, moderatedScores)
Building a functional pipeline
Trace the program above.
- Apply the filter condition
score >= 60 to the original list [42, 76, 58, 91, 63], giving [76, 91, 63].
- Apply the map function
score -> score + 2 to each remaining score, giving [78, 93, 65].
- Fold the moderated list with addition from starting accumulator
0: 0 + 78 gives 78, then 78 + 93 gives 171, then 171 + 65 gives 236.
- The final value of
total is 236.
Here is the same idea in Python 3:
from functools import reduce
scores = [42, 76, 58, 91, 63]
passing_scores = list(filter(lambda score: score >= 60, scores))
moderated_scores = list(map(lambda score: score + 2, passing_scores))
total = reduce(lambda accumulator, score: accumulator + score, moderated_scores, 0)
Python 3 map and filter
In Python 3, map and filter produce iterable objects. Use list(...) when you want to display or store the actual list of results.
When you read a problem, decide what kind of job is being described:
- “Apply this calculation to every item” usually means
map.
- “Keep only the items that meet this condition” usually means
filter.
- “Combine all items into one total, maximum, string, or result” usually means
reduce or fold.
Changing the order without checking meaning
filter then map may not mean the same thing as map then filter. For example, filtering passing scores before adding bonus marks is different from adding bonus marks before checking who passed.
In the exam
- Identify whether the task is transforming items, selecting items, or combining items; then choose
map, filter, or reduce/fold.
- When tracing, write the intermediate list after each operation rather than trying to jump straight to the final answer.
- For higher-order functions, explicitly say whether a function is being passed in, returned, or both.
Check yourself
- What is the difference between
map and filter?
- Why is
fold described as reducing a list to a single value?
- How can a function be higher-order even if it never processes a list?