In functional languages such as Scheme
or Lisp
there exist for
and for-all
loops. However for
loops require mutation since it's not a new stack frame each iteration. Since mutation is not available in these languages explicitly how do these functional languages implement their respective iterative loops?
-
3*Since mutation is not available in these languages explicitly* : that part is false – coredump Mar 14 '18 at 07:49
-
in pure languages mutation is emulated by state passing, working in concert with looping being emulated by tail recursion. Prolog is one example. – Will Ness Mar 15 '18 at 16:29
2 Answers
Scheme loops are implemented using recursion under the hood; constructs such as do
are just macros that get translated to recursive procedures. For example, this loop in a typical procedural language:
void print(int n) {
for (int i = 0; i < n; i++) {
display(i);
}
}
... Is equivalent to the following procedure in Scheme; here you can see that each part of the loop (initialization, exit condition, increment, body) has a corresponding expression:
(define (print n)
(define (loop i) ; helper procedure, a "named let" would be better
(when (< i n) ; exit condition, if this is false the recursion ends
(display i) ; body
(loop (+ i 1)))) ; increment
(loop 0)) ; initialization
Did you notice that there's nothing left to do after the recursion is called? the compiler is smart enough to optimize this to use a single stack frame, effectively making it as efficient as a for
loop - read about tail recursion for more details. And just to clarify, in Scheme mutation is explicitly available, read about the set!
instruction.

- 232,561
- 37
- 312
- 386
This question is really two questions, and a confusion.
Iteration in Scheme
In Scheme, iteration is implemented by recursion together with the semantics of the language mandating that certain kinds of recursion do not consume memory, in particular tail recursion. Note that this does not imply mutation. So, for instance, here is a definition of a while
loop i Racket.
(define-syntax-rule (while test form ...)
(let loop ([val test])
(if val
(begin
form ...
(loop test))
(void))))
As you can see the recursive call to loop
is in tail position and thus consumes no memory.
Iteration in traditional Lisps
Traditional Lisps do not mandate tail-call elimination and thus require iterative constructs: these are generally provided by the language, but usually can be implemented in terms of lower-level constructs, such as GO TO. Here is a definition of while
in Common Lisp which does this:
(defmacro while (test &body forms)
(let ((lname (make-symbol "LOOP")))
`(tagbody
,lname
(if ,test
(progn
,@forms
(go ,lname))))))
A confusion about mutation
Both Scheme and traditional Lisps provide mutation operators: neither are the pure functional languages that you may think they are. Scheme is closer to being one, but it still isn't very close.