Evaluating Functional Programming
advanced35 minLearning objectives
- Evaluate the advantages, disadvantages and applications of functional programming within modern software development
- Compare procedural, object-oriented and functional solutions to the same problem against explicit constraints
- Justify a paradigm choice for a given scenario without asserting universal superiority
Learn
AQA 4.12.1 — Evaluating functional programming against procedural and object-oriented approaches
Retrieval: Sequence 11's "Procedural Programming Revisited" built a student-record system as plain dictionaries plus standalone functions, then evaluated its coupling problem; "Introduction to OOP" then rebuilt similar data as a Student class. This lesson brings back that same problem — student records — a third time, solved functionally, so the three paradigms can finally be compared on genuinely equal ground rather than in the abstract.
Key vocabulary
- Maintainability — how easily code can be correctly modified as requirements change.
- State management — how a program tracks and controls values that change over its lifetime.
- Testability — how easily a piece of code can be tested in isolation, with predictable results.
- Reusability — how easily existing code can be applied to a new, different situation without rewriting it.
See it — the same problem, three paradigms
# Procedural (Sequence 11) - functions operate on a separate dictionary
def create_student(name, grades):
return {"name": name, "grades": grades}
def average_grade(student):
return sum(student["grades"]) / len(student["grades"]) if student["grades"] else 0
# Object-oriented (Sequence 11) - data and behaviour bundled into one object
class Student:
def __init__(self, name, grades):
self.name = name
self.grades = grades
def average_grade(self):
return sum(self.grades) / len(self.grades) if self.grades else 0
# Functional - a pure function; no class or dictionary "owns" the data
def average_grade(grades):
return sum(grades) / len(grades) if grades else 0
# used as: average_grade(student_grades) - grades passed explicitly, nothing bundled
The functional version is the simplest of the three, and also the most limited: it has no way to store a student's name alongside their grades at all — that grouping, if needed, has to happen somewhere else. This is a genuine, honest trade-off, not a flaw specific to the functional version.
Compare — against explicit constraints
| Constraint | Procedural | Object-oriented | Functional |
|---|---|---|---|
| State management | Data (dictionary) and functions stay separate; nothing stops code elsewhere from corrupting the dictionary's structure. | State is bundled and (with encapsulation conventions) protected inside the object; but state changing over time is itself a source of bugs if not carefully controlled. | No mutable state to manage at all — every function only ever produces new values, so there's nothing shared to corrupt. |
| Testability | Straightforward if the dictionary shape is simple and stable. | Requires constructing a full object first, even to test one small method. | Easiest of the three — average_grade(grades) needs nothing but its own arguments to test. |
| Reusability | Functions are reusable, but tied to the dictionary's exact key names throughout. | Methods are tied to the class; reusing just one piece of logic elsewhere means either subclassing or copying it out. | Highly reusable — a pure function with no dependency on any particular object's structure can be applied anywhere the right values exist. |
| Readability at scale | Clear for a handful of functions; harder to trace which function is responsible for which change as the system grows (Sequence 11's own coupling finding). | Clear responsibility boundaries once there are many related behaviours to bundle. | Clear for individual transformations; a long chain of composed map/filter calls can become hard to read at a glance if overused. |
| Complexity for this specific task | Low — a single function and a dictionary is genuinely enough here. | Higher than necessary here — defining a whole class for one dictionary-shaped record is arguably over-engineered at this size. | Low — a single pure function, arguably the simplest of the three for exactly this task. |
Evaluate — why "functional is better" is not the right conclusion
Notice the table above does not award every row to the same paradigm. Object-oriented programming's bundled, protected state is a genuine strength for a system where many related behaviours act on the same evolving data over a long lifetime (Sequence 11's Library, lending and returning books repeatedly) — exactly the situation functional programming's stateless approach handles awkwardly, because something, somewhere, still has to track which books are currently on loan. Functional programming's easy testability and reusability are genuine strengths for self-contained calculations with no real state to manage — but forcing a system with genuinely long-lived, evolving state (a library's live loan records) into a purely functional shape doesn't remove that state, it just makes tracking it someone else's, less protected, problem.
Justify it — recommend a paradigm, with reasons
For each scenario, recommend procedural, object-oriented or functional, and justify your choice against at least two of the constraints above:
- A one-off script that reads a CSV of exam scores and prints the class average.
- A library-management system that must track which books are currently on loan, to whom, and for how long, across an entire school year.
- A reusable utility that converts a list of temperatures from Celsius to Fahrenheit, used in several unrelated parts of a larger program.
(1: procedural - low complexity is the dominant constraint here; a one-off script has no long-term maintainability concern and doesn't need OOP's state-protection or FP's composability for a task this narrow. 2: object-oriented - state management is dominant; loan records must persist and change correctly over a long lifetime, exactly OOP's encapsulated-mutable-state strength, and exactly what a purely functional approach would still have to solve some other way, not avoid. 3: functional - reusability and testability are dominant; a pure conversion function has no state to manage at all, and being usable from anywhere without needing an object or a specific dictionary shape is precisely the point.)
Common mistake
Treating this lesson's message as "functional programming is the modern/superior choice." It is not — the comparison table above shows genuine strengths for procedural (simplicity, for small tasks) and object-oriented (protected, evolving state) that functional programming does not simply out-perform. A good evaluation names the specific constraint that matters most for the specific problem, not a general paradigm ranking.
Check your understanding
A colleague says: "We should rewrite our entire library system in a purely functional style, because functional programming is more modern." Evaluate this claim in two or three sentences. (This claim treats functional programming as universally superior rather than weighing it against this specific system's needs. The library system's core requirement - correctly tracking mutable state (loan records) over a long lifetime - is precisely what object-oriented programming's encapsulated state is well-suited to, and what a purely functional rewrite would still have to solve, just without OOP's built-in state protection. "More modern" is not itself a reason to switch paradigms; the actual constraints (state management, in this case) are.)
Challenge
Choose one coding challenge you completed earlier in this course that was written procedurally or object-orientedly. Evaluate, with specific reference to at least two constraints from this lesson's table, whether a functional rewrite would genuinely improve it, or whether the original paradigm was already the right choice.
Why this matters for the NEA
AQA's NEA explicitly rewards justified design decisions, not simply "correct" ones — being able to argue, with named criteria, why a particular paradigm suits your specific project (not merely which one you happen to know best) is directly transferable to your own NEA's design-justification sections.
This sequence's functional programming content is now complete. A synoptic assessment combining this paradigm-comparison work with Sequence 11's object-oriented content remains planned for later in the course, as previously discussed.
Looking ahead: Sequence 19 (Synoptic Integration) draws together tools from across the whole course — including all three paradigms compared honestly here — into genuinely open-ended problems that don't tell you in advance which one to reach for.