Recognizing a tail position
Tail position depends on the work remaining after a call returns, not on where a line appears in a file. Directly returning another call’s result can place that call in tail position. Adding one afterward leaves pending computation, even if the call appears close to a return statement.
Avoiding redundant return contexts
An ordinary call preserves information about where its caller should resume. A tail call can deliver its result to the same continuation without an extra return layer. Scheme requires support for an unbounded number of active tail calls, making proper tail recursion an implementation obligation rather than an occasional optimization.
References: [1]
Using an accumulator
A recursive sum can add the current element after summing the rest, leaving one pending addition per call. An accumulator instead carries the partial sum into the next call, whose result is returned directly. The mathematical task is unchanged, but the second arrangement moves work out of the returning control chain.
References: [2]
Control space is not all memory
Bounded control space does not imply constant total memory. A tail-recursive program can still construct a growing list or retain large values. Languages without the same guarantee may behave differently. Performance analysis must distinguish tail position, implementation commitments, and data growth instead of equating every recursive definition with stack overflow.