See it run
LISPEX
(let loop ((xs (list 1 2 3 4)) (sum 0)) (if (null? xs) sum (loop (cdr xs) (+ sum (car xs)))))Observed result
OUTPUT
10Walk through the pattern
- Find the base case. When
xsis empty, the answer already sits insum. - Shrink the remaining work.
(cdr xs)removes exactly one item, so every finite proper list reaches the base case. - Carry the answer forward. The next accumulator is computed before
loop, leaving the recursive call as the final action.
How to reason about it
- Carry accumulators as parameters instead of combining work after the recursive return.
- Tail position survives final
if,begin,and/or,cond,case,do,call-with-values, andapplypaths. - Use a bounded input during testing and distinguish continuation depth from runtime cost.
Check yourself
Add 5 to the input list. What result should the program produce, and does the recursive call remain in tail position?
Answer
The result is 15. Only the input data changes; loop is still the selected branch’s final action.
A common mistake
Tail safety does not make an infinite loop terminate or remove resource budgets.
Keep going
Use the tail-call manual when converting a non-tail function, and the procedure reference when choosing a fold instead.