[parenscript-devel] Closures inside loops: when should values be captured?

Daniel Gackle danielgackle at gmail.com
Thu Apr 5 23:20:10 UTC 2012


When a closure is created inside a loop, PS wraps the closed-over
variables using a JS WITH statement to ensure that each closure will get
the values that existed at the time it was created. Here's an example
from Vladimir's email to the list from Jan 30, 2011:

   (let ((foo (make-array 3)))
     (dotimes (i 3)
       (setf (aref foo i) (lambda () i)))
     (funcall (@ console log) (funcall (aref foo 0)))
     (funcall (@ console log) (funcall (aref foo 1)))
     (funcall (@ console log) (funcall (aref foo 2))))

This correctly prints 0,1,2 to the console. If PS didn't do this extra
work and relied on JS scoping, the output would be 3,3,3 which is
obviously (or at least arguably) incorrect.

But this doesn't happen for variables declared prior to the loop form.
Here is the above example written to use WHILE rather than DOTIMES:

   (let ((foo (make-array 3))
         (i 0))
     (while (< i 3)
       (setf (aref foo i) (lambda () i))
       (incf i))
     (funcall (@ console log) (funcall (aref foo 0)))
     (funcall (@ console log) (funcall (aref foo 1)))
     (funcall (@ console log) (funcall (aref foo 2))))

It prints 3,3,3. Of course, probably no one would use WHILE to write
this particular loop, but it's easy to see how one might run in to the
problem. In particular, the PS LOOP macro generates WHILE forms for
many standard kinds of loop. That is how I ran across this issue: I
have a LOOP form that builds a table of closures for asynchronous
callback in a Node.js program and by the time these closures are
called back, they no longer have their expected data - for exactly the
same reason as the above example prints 3,3,3.

My first question is: is this a bug? That is, if it's incorrect for the
first loop to print 3,3,3, is it also incorrect for the second loop to
do so?

Daniel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/parenscript-devel/attachments/20120405/08a6e44d/attachment.html>


More information about the parenscript-devel mailing list