AQA 7517 · A-Level Computer Science · ~15 min read
Closures
A closure is a function that captures variables from the scope in which it was defined — even after that scope has finished executing. The function "closes over" the free variables.
-- makeAdder returns a function that adds n to its argumentmakeAdder n = \x -> n + x
add5 = makeAdder 5-- add5 is a closure over n=5add53-- returns 8add510-- returns 15
Here, add5 remembers the value n=5 even though makeAdder has finished. This is a closure.
Currying and Partial Application
Currying is the technique of transforming a function that takes multiple arguments into a chain of functions each taking a single argument.
-- Uncurried: takes both args at onceadd (x, y) = x + y
-- Curried: takes one arg, returns function expecting anotheraddC x y = x + y
addC3-- returns function \y -> 3 + yaddC34-- returns 7
Partial application: supplying fewer arguments than a function expects, producing a new function waiting for the remaining arguments. In Haskell, all multi-argument functions are curried by default.
-- Partial application examplemultiply x y = x * y
triple = multiply 3-- partially applied: triple y = 3 * ytriple5-- returns 15triple9-- returns 27
Recursion in Functional Programming
Because functional programming avoids mutable state, loops are replaced with recursion. The function calls itself with a smaller input until reaching a base case.
A recursive call is tail recursive if the recursive call is the very last operation — no work is done after the call returns. Compilers can optimise tail recursion into a loop (tail call optimisation, TCO), preventing stack overflow for large inputs.
Functional languages process lists recursively using head/tail patterns:
head: first element of a list — head [1,2,3] = 1
tail: all elements except the first — tail [1,2,3] = [2,3]
-- Sum a list recursivelysumList [] = 0-- base case: empty listsumList (x:xs) = x + sumList xs -- x is head, xs is tailsumList [1,2,3]
-- = 1 + sumList [2,3]-- = 1 + 2 + sumList [3]-- = 1 + 2 + 3 + sumList []-- = 1 + 2 + 3 + 0 = 6
Lazy Evaluation
Lazy evaluation (also called call-by-need): expressions are not evaluated until their value is actually needed. This allows:
Infinite data structures (e.g. an infinite list of natural numbers)
Efficiency — avoids computing values that are never used
-- Haskell: infinite list of natural numbers (lazy)nats = [1..] -- infinite list, never fully evaluatedtake5 nats -- [1, 2, 3, 4, 5] — only first 5 evaluated
Exam tip: AQA 7517 requires: (1) explain closure and trace an example; (2) explain currying/partial application — know that currying transforms multi-arg functions into single-arg chains; (3) trace recursive functions with base case + recursive case; (4) explain tail recursion and why it avoids stack overflow; (5) trace list processing with head/tail. These topics appear as trace + explain questions worth 4–6 marks.
▶
Click through the slides at your own pace. Use arrow keys or click to advance.
Click slide or press arrow keys to navigate
Worksheet — 4.12.1b Functional Programming in Practice
8 questions · instantly marked · AQA 7517 standard
Q1What is a closure in functional programming? Explain using an example.[2]
✅ Mark scheme
Mark scheme
A closure is a function that captures and retains access to variables from the scope in which it was defined — even after that scope has finished executing [1]; the function "closes over" the free variables [1]. Example: makeAdder n = \x -> n + x; add5 = makeAdder 5 [1]; add5 3 returns 8 — add5 remembers n=5 even though makeAdder has returned [1].
Q2Explain what currying is. How does it differ from partial application?[3]
✅ Mark scheme
Mark scheme
Currying: transforming a function that takes multiple arguments into a chain of functions each taking a single argument [1]; e.g. add x y = x+y is curried — add 3 returns a function \y->3+y [1]. Partial application: supplying fewer arguments than a function expects to produce a new function [1]; e.g. triple = multiply 3 — multiply is partially applied with 3, producing a function that triples its argument [1].
Q3Trace factorial 3 using the recursive definition. Show every step.[2]
Q4Explain what tail recursion is and why it is more efficient than regular recursion.[3]
✅ Mark scheme
Mark scheme
Tail recursion: the recursive call is the very last operation in the function — no computation is done after the recursive call returns [1]. Regular recursion builds up a stack of pending return values — for large inputs this causes stack overflow [1]. Tail recursion allows tail call optimisation (TCO): the compiler replaces the recursive call with a loop — the stack frame is reused rather than a new one pushed for each call [1]; this uses O(1) stack space instead of O(n), preventing stack overflow [1].
Q6In Haskell: multiply x y = x * y. What does triple = multiply 3 give? What is triple 7?[2]
✅ Mark scheme
Mark scheme
triple = multiply 3 is partial application — multiplying 3 is supplied, returning a new function that multiplies its argument by 3 [1]; triple y = 3 * y [1]; triple 7 = 3 * 7 = 21 [1].
Q7What is lazy evaluation? Give one advantage it provides in functional programming.[2]
✅ Mark scheme
Mark scheme
Lazy evaluation: expressions are not evaluated until their value is actually needed (call-by-need) [1]. Advantage (any 1): allows infinite data structures — only the portion actually needed is evaluated; e.g. take 5 [1..] works even though [1..] is an infinite list [1]; efficiency — avoids computing values that are never used by the rest of the program [1].
Q8Define a recursive function length' that returns the number of elements in a list. Trace length' [4, 7, 2] and give the result.[3]
✅ Mark scheme
Mark scheme
Definition: length' [] = 0 [1]; length' (_:xs) = 1 + length' xs [1] (the head element is discarded with _ since we only count, not use it). Trace: length' [4,7,2] = 1 + length' [7,2] [1]; = 1 + 1 + length' [2] = 1 + 1 + 1 + length' [] = 1+1+1+0 [1]; Result: 3 [1].
Functional Programming in Practice Quiz
Question 1 of 15
You scored
out of 15
Card 1 of 8
Click to reveal definition
🎉
All cards reviewed!
Term
Definition
🎯
Mini Test — Closures, Currying & Recursion
10 questions · 10 minutes
⏱ 10:00
Section A — Multiple Choice [5 marks]
Q1A closure is best described as:
Q2Currying transforms a function by:
Q3factorial 0 = 1; factorial n = n * factorial (n-1). What is factorial 3?
Q4Tail call optimisation (TCO) allows tail-recursive functions to:
Q5head [5, 10, 15] returns:
Section B — Short Answer [5 marks]
Q6What is a closure?
Mark schemeA function that captures (retains access to) variables from its enclosing scope — even after that scope has finished executing [1].
Q7What is partial application? Give a brief example.
Mark schemeSupplying fewer arguments than a function expects — the result is a new function waiting for the remaining arguments [1]; e.g. triple = multiply 3 → triple y = 3*y [1].
Q8Why does regular recursion risk stack overflow for large inputs?
Mark schemeEach recursive call adds a new frame to the call stack; for large n, the stack fills up before the base case is reached, causing stack overflow [1].
Q9What does tail [4, 8, 12] return?
Mark scheme[8, 12] — tail returns all elements except the first (the head) [1].
Q10What is lazy evaluation and what does it enable?
Mark schemeExpressions are evaluated only when their value is needed [1]; enables infinite data structures (only evaluated as far as required) and avoids unnecessary computation [1].