To directly answer your question, "next
doesn't get reset to 0
every time count
is used" because your code
(define count (let ((next 0))
(lambda ()
(let ((v next))
(set! next (+ next 1))
v))))
is equivalent to
(define count #f)
(set! count ( (lambda (next) ; close over `next`
(lambda () ; and then return lambda which _will_
(let ((v next)) ; read from `next`,
(set! next (+ v 1)) ; write new value to `next`,
v))) ; and then return the previous value;
0 )) ; `next` is initially 0
(this is "let-over-lambda" pattern; there's even a Lisp book by that name).
The value to assign count
is "calculated" only once. This value is a closure referring to the binding for next
, which (binding) is external to it (the closure). Then every time count
is "used", i.e. a procedure that it refers to is called, it (the procedure) refers to that binding: first it reads from it, then it alters its contents. But it doesn't re-set it to its initial value; the binding is initiated only once, as part of its creation, which happens when the closure is created.
This binding is visible only from this procedure. The closure is a bundle of this procedure and the environment frame holding this binding. This closure is the result of evaluating the (lambda () ...)
expression inside the lexical scope of next
, the (lambda (next) ...)
expression.