You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
Functional programming (FP) is a programming paradigm where programs are constructed by applying and composing functions. Unlike imperative programming, which describes how to do something step by step with mutable state, functional programming focuses on what to compute by evaluating mathematical functions without side effects.
A paradigm is a style or approach to programming. The three main paradigms you need to know for A-Level are:
| Paradigm | Description | Examples |
|---|---|---|
| Imperative / Procedural | Step-by-step instructions that change program state | C, Pascal, Python (procedural style) |
| Object-Oriented | Models real-world entities as objects with attributes and methods | Java, C#, Python (OOP style) |
| Functional | Computation through function application with no mutable state | Haskell, Lisp, Erlang, F# |
Many modern languages are multi-paradigm — Python, JavaScript, and Scala support both imperative and functional styles.
In FP, functions are the fundamental unit of computation. Programs are built by defining functions and combining them, rather than by writing sequences of instructions.
A side effect is any observable change outside the function — modifying a global variable, writing to a file, printing to the screen, or changing the input data. In pure functional programming, functions have no side effects.
Data is immutable — once a value is assigned, it cannot be changed. Instead of modifying existing data, you create new data structures with the desired changes.
FP uses a declarative style: you describe what result you want, not how to compute it step by step.
Imperative (how):
# Sum the squares of even numbers from 1 to 10
total = 0
for i in range(1, 11):
if i % 2 == 0:
total += i * i
Functional (what):
# Same computation, declarative style
total = sum(x*x for x in range(1, 11) if x % 2 == 0)
| Feature | Imperative | Functional |
|---|---|---|
| State | Mutable — variables change over time | Immutable — values do not change |
| Control flow | Loops (for, while), conditionals | Recursion, function composition |
| Side effects | Common (print, file I/O, mutations) | Avoided — pure functions have none |
| Primary abstraction | Procedures / methods | Functions |
| How vs What | Describes how to compute | Describes what to compute |
| Data | Modified in place | New data created from old data |
Easier to reason about. Since functions always produce the same output for the same input (referential transparency), you can understand and predict behaviour more easily.
Easier to test. Pure functions with no side effects are straightforward to test — provide an input, check the output.
Better for parallelism. With no shared mutable state, functional programs can be safely run across multiple processors without race conditions.
Mathematical foundation. FP is rooted in lambda calculus, a formal system developed by Alonzo Church in the 1930s.
Growing industry adoption. Functional concepts are increasingly used in mainstream languages. Map, filter, and reduce are now standard in Python, JavaScript, Java, and C#.
Functional programming is based on lambda calculus (the lambda calculus), a mathematical model of computation.
In lambda calculus, everything is expressed as:
Haskell uses this notation directly:
-- A lambda function that doubles a number
double = \x -> x * 2
-- Applying it
double 5 -- Result: 10
In mathematics, a function maps values from a domain (set of valid inputs) to a co-domain (set of possible outputs).
Example: The function f(x) = x^2 where x is a positive integer:
In Haskell, types serve as domains and co-domains:
square :: Int -> Int
square x = x * x
This declares that square takes an Int (domain) and returns an Int (co-domain).
Scenario: Compare an imperative approach and a functional approach to finding all even numbers in a list and doubling them.
Imperative (Python):
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = []
for n in numbers:
if n % 2 == 0:
result.append(n * 2)
# result = [4, 8, 12, 16, 20]
Functional (Python):
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = list(map(lambda n: n * 2, filter(lambda n: n % 2 == 0, numbers)))
# result = [4, 8, 12, 16, 20]
Functional (Haskell):
result = map (*2) (filter even [1..10])
-- result = [4, 8, 12, 16, 20]
Notice how the functional versions describe what to do (filter even numbers, then double them) rather than how (loop through, check, append).