How do I write a recursive function in Racket?

Short answer

Every recursive function needs a base case that returns without recursing, and a recursive case that moves measurably closer to it. For lists, that is empty? and rest.

racket
(define (sum-list lst)
  (if (empty? lst)
      0                                        ; base case
      (+ (first lst) (sum-list (rest lst)))))  ; recursive case

rest always returns a shorter list, so empty? is guaranteed to be reached.

#Trace it by substitution

text
(sum-list '(1 2 3))
(+ 1 (sum-list '(2 3)))
(+ 1 (+ 2 (sum-list '(3))))
(+ 1 (+ 2 (+ 3 (sum-list '()))))
(+ 1 (+ 2 (+ 3 0)))
6

Every step is "replace an expression with its value". Nothing off-screen can change the answer — which is why Racket is used to teach this.

#The template for list recursion

racket
(define (process lst)
  (cond
    [(empty? lst) BASE-VALUE]
    [else (COMBINE (first lst) (process (rest lst)))]))

Fill in the two blanks and you have written length, sum, map, filter and reverse.

#Accumulator style

The version above builds up pending work on the stack. An accumulator carries the answer down instead:

racket
(define (sum-list lst [acc 0])
  (if (empty? lst)
      acc
      (sum-list (rest lst) (+ acc (first lst)))))

This is tail recursive — the recursive call is the last thing that happens — and Racket optimises it into a loop, so it runs in constant stack space.

#When it goes wrong

Infinite recursion means the problem is not shrinking. Check that you are recursing on (rest lst) and not lst.

"expected a list" errors usually mean the base case is missing, so rest was called on '().