Erm, they work the same way in Common Lisp (tested with CCL):
CL-USER> (defvar *id* 0)
ID
CL-USER> (defun closure-test ()
(incf *id*)
(lambda ()
(format t "id: ~a " *id*)))
CLOSURE-TEST
CL-USER> (defvar test-list (loop repeat 3 collecting (closure-test)))
TEST-LIST
CL-USER> (mapc #'funcall test-list)
id: 3 id: 3 id: 3
(let* ((closure-id 0)
(closure-list (loop repeat 3 collecting
(progn
(incf closure-id)
(lambda ()
(format t "id: ~a~%" closure-id))))))
(mapc #'funcall closure-list))
id: 3
id: 3
id: 3
If you want to enclose a value in Common Lisp, instead of a variable, you're going to have to shadow the externally defined variable
(let* ((closure-id 0)
(closure-list (loop repeat 3 collecting
(progn
(incf closure-id)
(let ((closure-id closure-id)) ;Note, only works on lexically scoped variables, don't use the same name for something you defvar'ed. Another use for those *'s
(lambda ()
(format t "id: ~a~%" closure-id)))))))
(mapc #'funcall closure-list))
id: 1
id: 2
id: 3
Of course, we can easely define a macro to do that for us:
(defmacro capture (variables &body code)
"Use this macro around a function definition to have it enclose the value of lexically scoped variables instead of the variables themselves"
`(let (,@(loop for var in variables collecting (list var var)))
,@code))
Which turns the above code into:
(let* ((closure-id 0)
(closure-list (loop repeat 3 collecting
(progn
(incf closure-id)
(capture (closure-id)
(lambda ()
(format t "id: ~a~%" closure-id)))))))
(mapc #'funcall closure-list))
Which saves a few keystrokes and makes it clear we're trying to enclose closure-id in our closure (of course you could combine the anonymous function definition with the capture, but that's left as an exercise to the reader or at least someone less lazy then I am)
(Note that this macro will not work if you define the ID using defvar, because that means ID is dynamically scoped. The easiest way to fix that is probably to add a suffix to the local variables. I'm not sure if you can shadow a dynamic variable with a lexical one, but if you can, then that'd be a better option)
There is an important reason that closures don't capture values instead of variables, because if they did they would behave differently from normal functions, which brings in it's own set of headaches
(not to mention that you can't have the classic lisp example:
(let ((counter 0))
(list (lambda ()
(incf counter))
(lambda ()
(setf counter 0))))
which defines two closures sharing the same variable, one giving you the next integer (for numeric ID's or something) and the second being usable to reset the counter.)
Edit: Noticed I was using mapcar and mapc interchangeably. Cleaned that up to avoid confusion and because mapc is more appropriate in this case.
Edit 2: Figured out one way to make capture work with dynamic variables:
(defmacro capture (variables &body code)
`(let (,@(loop for var in variables collecting (list (intern (concatenate 'string "LOCAL-" (symbol-name var))) var)))
,@code))
By getting the symbol-name of the variable that's being captured we can concatenate local to it (needs to be uppercase or else the name will be |local-*| instead of local-*), meaning the value of for example x is bound to local-x. Of course this is under the same reservation as any anaphoric macro (macro's that bind variables for you), namely that you need to make sure you don't get name interference while using it.