this section outlines the main choices when designing loops. recursion, iteration, and higher-order forms like map or fold are interchangeable; the key is understanding when and how to use each based on termination, state, and structure.
each loop construct can be analyzed along the following axes:
termination: how and when the loop stops
state management: what data is updated across iterations
composition style: how the loop integrates with surrounding code
control flow abstraction: how much of the control is abstracted
full control: explicit loop or recursion
partial abstraction: fold, unfold, etc.
total abstraction: lazy sequences, generators
map: applies a function to each element and returns a new structure
fold/reduce: accumulates a result by applying a function across elements
unfold: generates a structure from a seed by repeated transformation
custom recursive loop: uses a named recursive binding (e.g. let loop
)
full control over state and branching
allows multiple outputs and mixed control flows
in scheme
(let loop ((rest (list 1 2 3))) (if (null? rest) rest (cons (+ 1 (car rest)) (loop (cdr rest)))))
this implements a basic map operation by:
(null? rest)
loop
with (cdr rest)
cons
and (+ 1 ...)
this style generalizes easily - for example, by accumulating additional values, branching conditionally, or logging state.
identify the stop condition early
determine what state changes per iteration
decide how much abstraction is acceptable
map
, fold
let loop
consider composition