The Goldbach Conjecture is one of the oldest unsolved problems in mathematics. It states that every even integer greater than 2 can be expressed as the sum of two prime numbers.
While no formal proof has been found, the conjecture has been verified by computers for very large numbers. In this post, we’ll explore how to express this idea recursively in Haskell — using an elegant search through the space of prime pairs.
For any even number x > 2, find two primes p1 and p2 such that:
p1 + p2 = x
For example:
4 = 2 + 26 = 3 + 310 = 5 + 5 or 7 + 3The challenge is to find such a pair efficiently — and recursion provides a natural way to iterate through potential candidates.
Let’s look at my implementation of the Goldbach function in Haskell:
goldbach :: Int -> (Int, Int)
goldbach x = subGoldbach x 1 (subGoldbachDesc x)
subGoldbach :: Int -> Int -> Int -> (Int, Int)
subGoldbach x v1 v2 =
let (newv1, newv2) = updateInterval v1 v2
in if (v1 + v2) == x
then (v1, v2)
else subGoldbach x newv1 newv2
updateInterval :: Int -> Int -> (Int, Int)
updateInterval v1 v2
| v1 == v2 = (1, subGoldbachDesc v2)
| otherwise = (subGoldbachAsc (v1 + 1) v2, v2)
subGoldbachDesc :: Int -> Int
subGoldbachDesc 1 = 1
subGoldbachDesc x
| isPrime x = x
| otherwise = subGoldbachDesc (x - 1)
subGoldbachAsc :: Int -> Int -> Int
subGoldbachAsc x cmp
| x == cmp = cmp
| isPrime x = x
| otherwise = subGoldbachAsc (x + 1) cmp
Note that the function isPrime is assumed to exist — it checks if a number is prime.
The rest of the program is a clean, recursive search over possible prime pairs.
The entry point goldbach begins with two values:
v1 = 1 — the lower bound that will grow upward.v2 = subGoldbachDesc x — the largest prime less than or equal to x.
Then, subGoldbach checks whether the current pair (v1, v2) satisfies the Goldbach condition:
v1 + v2 == x → success! We found a valid pair.v1 and v2 and try again.
The function updateInterval moves our search boundaries:
v1 == v2), we reset v1 and find a smaller upper prime.v1 + 1 using subGoldbachAsc.
Two helper functions are at the heart of the search: subGoldbachDesc and subGoldbachAsc.
subGoldbachDesc finds the largest prime less than or equal to x:
subGoldbachDesc :: Int -> Int
subGoldbachDesc 1 = 1
subGoldbachDesc x
| isPrime x = x
| otherwise = subGoldbachDesc (x - 1)
Meanwhile, subGoldbachAsc finds the next prime greater than or equal to a given number, but not exceeding a comparison limit:
subGoldbachAsc :: Int -> Int -> Int
subGoldbachAsc x cmp
| x == cmp = cmp
| isPrime x = x
| otherwise = subGoldbachAsc (x + 1) cmp
These two recursive helpers define the upward and downward motion through the prime number space, continuously adjusting our pair candidates until a valid combination is found.
Think of the algorithm as two pointers moving inside a shrinking window:
At each recursive step:
v1 + v2 == x.v1 upward to the next prime.v1 catches up to v2, move v2 downward to the next smaller prime and restart v1.Eventually, recursion guarantees that all possible pairs of primes are checked, and the first valid one is returned as the Goldbach decomposition.
Let’s run an example:
main = print (goldbach 28)
Suppose subGoldbachDesc 28 gives us v2 = 23.
Then recursion begins:
The function returns (5, 23), one valid Goldbach pair for 28.
The structure of the Goldbach problem is naturally recursive: each step depends only on a smaller subproblem — finding the next suitable primes.
subGoldbachDesc and subGoldbachAsc handle prime traversal recursively.subGoldbach recursively tests combinations of primes.v1 + v2 == x is met.It’s a perfect example of functional recursion as an elegant search mechanism — no mutable state, no explicit loops, just pure logical flow.
The Goldbach Conjecture may remain unproven, but in Haskell, we can express its logic clearly and functionally.
Whether or not Goldbach is ever proven true, one thing is certain: recursion in Haskell provides one of the cleanest ways to think like a mathematician in code.