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
That's the correct behavior because you're closing over the same binding of i in all iterations. This equivalent CL code:
(let ((foo (make-array 3)) (i 0)) (loop while (< i 3) do (setf (aref foo i) (lambda () i)) (incf i)) (funcall #'princ (funcall (aref foo 0))) (funcall #'princ (funcall (aref foo 1))) (funcall #'princ (funcall (aref foo 2))))
Also does 3 3 3
Vladimir
On Thu, Apr 5, 2012 at 7:20 PM, Daniel Gackle danielgackle@gmail.com wrote:
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
parenscript-devel mailing list parenscript-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
Thanks. I noticed the same thing about CL, but wasn't clear on the principle. Now I think I understand. The salient question is: was the variable declared inside the loop? If it was, there's an implicit request to get a new binding each time the loop executes. If it wasn't, all closures over the variable will share the same binding.
On Fri, Apr 6, 2012 at 5:52 PM, Vladimir Sedach vsedach@gmail.com wrote:
That's the correct behavior because you're closing over the same binding of i in all iterations. This equivalent CL code:
(let ((foo (make-array 3)) (i 0)) (loop while (< i 3) do (setf (aref foo i) (lambda () i)) (incf i)) (funcall #'princ (funcall (aref foo 0))) (funcall #'princ (funcall (aref foo 1))) (funcall #'princ (funcall (aref foo 2))))
Also does 3 3 3
Vladimir
On Thu, Apr 5, 2012 at 7:20 PM, Daniel Gackle danielgackle@gmail.com wrote:
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
parenscript-devel mailing list parenscript-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
parenscript-devel mailing list parenscript-devel@common-lisp.net http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
parenscript-devel@common-lisp.net