Consider these two:
(defparameter *lfn*
(let ((count 0))
#'(lambda ()
(incf count))))
(defun testclosure ()
(let ((count 0))
#'(lambda ()
(incf count))))
Why do they behave differently:
CL-USER> (funcall (testclosure))
1
CL-USER> (funcall (testclosure))
1
CL-USER> (funcall *lfn*)
1
CL-USER> (funcall *lfn*)
2
count
is closed over in the defparameter
version but not in the defun
version. Why is this?
Sylwester's answer explains this very well, but in a case an example with more explicit side effects makes this any clearer, consider:
CL-USER> (defparameter *foo* (progn (print 'hello) 0))
HELLO
*FOO*
CL-USER> *foo*
0
CL-USER> *foo*
0
In defining *foo*
, the (progn (print 'hello) 0)
is evaluated once, so hello
is printed, and the value is 0
, which becomes the value of *foo*
. Evaluating *foo*
later just means looking up *foo*
's value (0
), not reëvaluating the form that produced its original value. In contrast, consider calling a function whose body is
(progn (print 'hello) 0)`:
CL-USER> (defun foo () (progn (print 'hello) 0))
FOO
CL-USER> (foo)
HELLO
0
CL-USER> (foo)
HELLO
0
CL-USER> (foo)
HELLO
0
Each time foo
is called, (progn (print 'hello) 0)
is evaluated, so hello
is printed and 0
is returned. After seeing this example, your code should be a bit clearer.
(defparameter *lfn*
(let ((count 0))
#'(lambda ()
(incf count))))
(let ...)
is evaluated once, and the closure that that evaluation produces its the value of *lfn*
. On the other hand, in
(defun testclosure ()
(let ((count 0))
#'(lambda ()
(incf count))))
(let ...)
is evaluated every time that testclosure
is called, a new closure is returned each time.
When you are making *lfn*
you are creating a function within one closure.. Calling it will increase the closed over count and evaluate to it.
testclosure
does the same as what you did with *lfm*
for every time it gets called. Thus:
(defparameter *lfn2* (testclosure))
(funcall *lfn2*) ; ==> 1
(funcall *lfn2*) ; ==> 2
(funcall *lfn2*) ; ==> 3
Will make exactly the same as *lfn*
such that consecutive calls to it will increase the value returned. However
(funcall (testclosure)) ; ==> 1 (and the closure can be recycled)
(funcall (testclosure)) ; ==> 1 (and the closure can be recycled)
Here you are doing funcall
on a newly created closure, that you don't store for consecutive calls, so it will return 1. Then you are doing funcall
again on a completely new closure that you also don't store and it's first call also evaluates to 1.
So the answer is, count is closed over in both, but in you example you were creating a new closure and using it only once, several times.
The value of *lfn*
is a closure.
The function testclosure
returns a new closure each time you call it.