Today I did a little more work on my slides for the MUG presentation, but I also read some more of the (excellent) manual. The section on Closures really helped me understand better what was going on.
For those playing along at home, a closure is when a function is called in such a way that the local state is preserved. So, for example:
(define (silly-function x) (let ((y (/ x 2))) y))
This defines a lexical scope for y where y does not exist outside of the scope of the function. If I did the following:
(define y "moo") (silly-function 8) => 4 y => "moo"
"y" is not overwritten.
What makes closure so powerful is best described in this example (taken from the Guile documentation):
(define (make-serial-number-generator) (let ((current-serial-number 0)) (lambda () (set! current-serial-number (+ current-serial-number 1)) current-serial-number))) (define entry-sn-generator (make-serial-number-generator)) (entry-sn-generator) => 1 (entry-sn-generator) => 2 ...
In this case
current-serial-number is initialized with 0 and the lambda function turns the function into a variable. Since the function as a whole is saved as a variable you can assign it to another variable and that variable holds the address of the instantiated function. So when you call that variable as a function it looks up the address of the function, brings up the current state of where it left off, and then (in this case) increments the value of current-serial-number.