Recursion is one of those ideas that trips people up at first, then clicks in a way that permanently changes how you think about problems. This is an attempt to explain why — and when to actually use it.

The basic idea

A recursive function is one that calls itself. The definition is almost disarmingly simple. The power comes from how you apply it.

def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

The key ingredient is a base case: a condition under which the function stops and returns a value directly. Without it you get infinite recursion, which is less useful.

Why it matters in functional programming

In languages like Haskell or Erlang, loops don’t exist as a primitive. Iteration is expressed through recursion instead. This isn’t a limitation — it’s a deliberate design choice that leads to clearer reasoning about programs.

Consider summing a list, written two ways:

# imperative
def sum_list(lst):
    total = 0
    for x in lst:
        total += x
    return total

# recursive
def sum_list(lst):
    if not lst:
        return 0
    return lst[0] + sum_list(lst[1:])

The recursive version says what the result is rather than how to compute it: the sum of a list is its first element plus the sum of the rest. This declarative quality is what makes recursion feel at home in functional code.

When to reach for it

Recursion shines when the problem is naturally self-similar — trees, nested structures, divide-and-conquer algorithms. For simple iteration over a flat sequence, a loop is usually clearer and more efficient.

The real skill is recognising which shape your problem has.

The definition of insanity is doing the same thing over and over and expecting different results. Recursion is the opposite: doing the same thing over and over with different input, expecting to eventually hit a base case.