You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
What is Functional Programming?
What is Functional Programming?
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.
Programming Paradigms
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.
Key Characteristics of Functional Programming
1. Functions as the Primary Building Block
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.
2. No Side Effects
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.
3. Immutability
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.
4. Declarative Style
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)
Functional vs Imperative: A Comparison
| 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 |
Why Study Functional Programming?
-
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#.
Lambda Calculus — The Foundation
Functional programming is based on lambda calculus (the lambda calculus), a mathematical model of computation.
In lambda calculus, everything is expressed as:
- Variables: x, y, z
- Abstractions (functions): \ x -> x + 1 (a function that takes x and returns x + 1)
- Applications: (\x -> x + 1) 5 evaluates to 6
Haskell uses this notation directly:
-- A lambda function that doubles a number
double = \x -> x * 2
-- Applying it
double 5 -- Result: 10
Domain and Co-domain
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:
- Domain: {1, 2, 3, 4, ...} (positive integers)
- Co-domain: {1, 4, 9, 16, ...} (perfect squares)
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).
Worked Example
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).
Exam Tips
- Be prepared to compare imperative and functional paradigms — examiners often ask for advantages and disadvantages of each.
- Know the key characteristics: immutability, no side effects, functions as first-class objects, declarative style.
- Understand that lambda calculus is the mathematical foundation of functional programming.
- Be able to explain domain and co-domain in the context of function types.
- Remember that functional programming is not "better" or "worse" than imperative — each has strengths for different problems.