[parenscript-devel] 'use strict' and 'with' in loop

Peter Wood p.r.wood at gmail.com
Sat Mar 2 08:47:01 UTC 2013


Hi Daniel


Thanks for the nice explanation.  It's a cool trick (although personally I
would prefer it if parenscript followed CL's scoping).  You can get the
desired '(10 20 30) behaviour in CL (I'm using SBCL) like this:

 (let ((closures (loop :for i :from 1 :to 3
                    :collect (let* ((j i)
                                    (cl (lambda () (* j 10))))
                               cl))))
   (loop :for fn :in closures :collect (funcall fn)))

==> (10 20 30)

However, when I tested this in a parenscript hacked not to generate the
'with ({i : i})' it didn't work (the generated js returns '[30, 30, 30]'.
It _does_ work in the vanilla parenscript, but also generates the offending
'with ({j . null})'.  Returning the closure from a function works the same
in CL and parenscript and does not generate the with object stuff.

(defun foo ()
           (labels ((make-cl (i) (lambda () (* i 10))))
             (let ((closures (loop :for i :from 1 :to 3
                                :collect (make-cl i))))
               (loop :for fn :in closures :collect (funcall fn)))))

(foo) ==> (10 20 30)

foo() ==> [10, 20, 30]

So, apart from using do, there is also a labels 'workaround' when I want to
'use strict';   Whatever you guys decide, thanks again for parenscript.
It's excellent and fun as well!

Regards,
Peter





On 2 March 2013 02:08, Daniel Gackle <danielgackle at gmail.com> wrote:

> Hi Peter,
>
> Welcome! The 'with' trick (which confused me when I first saw it as
> well) occurs when a loop contains a closure that captures the loop
> iteration variable:
>
>   (loop :for i :from 1 :to 3 :collect (lambda () (* i 10)))
>
> The question is what value of i each lambda should use when it's
> called. The 'with' trick establishes a new scope with a new binding
> for 'i' inside the loop body and puts the closure inside that scope.
> Thus, if you do this:
>
>   (let ((closures (loop :for i :from 1 :to 3 :collect (lambda () (* i
> 10)))))
>     (loop :for fn :in closures :collect (funcall fn)))
>
> ... you get '(10 20 30), because each closure remembers the value that
> i had when it was created. Without the 'with' trick, you'd get '(40 40 40),
> because the closures all share the loop's original binding for i, and
> that held 40 by the time the loop terminated.
>
> The fact that it breaks Strict mode, though, means that either PS's
> implementation should change, at least to offer the option of not
> using it, or drop the trick altogether. I have a feeling the latter
> would be simplest. For one thing, Common Lisp, which is PS's
> touchstone, doesn't have this scoping behavior. In CCL I get '(40 40 40)
> for the above expression. And DOTIMES is the same:
>
>   (let ((list nil))
>      (dotimes (i 3) (push (lambda () (* i 10)) list))
>      (mapcar #'funcall (reverse list)))
>
> => (30 30 30)
>
> So this is a case of plus royaliste que le roi that could arguably just be
> abandoned. If not, though, a special variable for Strict Mode would be
> a good idea. Vladimir?
>
> Daniel
>
>
> On Fri, Mar 1, 2013 at 12:39 PM, Peter Wood <p.r.wood at gmail.com> wrote:
>
>> Hi
>>
>> It's my first post, so first of all, thanks to everyone who works on
>> parenscript.  It is a lifesaver.
>>
>> If I 'use strict'; in the start of my scripts, they fail in some of the
>> loops because parenscript is generating a 'with' (which is not allowed in
>> strict mode).   There is quite a nice explanation of why it isn't allowed
>> here:
>>
>>
>> https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode
>>
>> It is easy to change my code to use 'do' instead, and for now, that's
>> what I've done, but it's a shame not to be able to use parenscript's very
>> nice loop for writing javascript loops.  I haven't looked at parenscript's
>> code before this evening, but I think this is the relevant spot (in
>> src/special-operators.lisp)
>>
>> (defun compile-loop-body
>> ...
>> (aif (sort (remove-duplicates *loop-scope-lexicals-captured*)
>>                #'string< :key #'symbol-name)
>>          `(ps-js:block
>>               (ps-js:with
>>                   ,(compile-expression
>>                     `(create
>>                       ,@(loop for x in it
>>                               collect x
>>                               collect (when (member x loop-vars) x))))
>>                 ,compiled-body))
>>          compiled-body)))
>>
>> Here is an example of some lisp and the js which it generates:
>>
>> (ps:ps (defun foo ()
>>                   (loop for i from 1 to 5
>>                      append (loop for j from 1 to 5
>>                                  collect (list i j)))))
>> ==>
>> "function foo() {
>>     return (function () {
>>         var append9 = [];
>>         for (var i = 1; i <= 5; i += 1) {
>>             with ({ i : i }) {
>>         ^^^^^^^^^^
>>                 append9 = append9.concat((function () {
>>                     var collect10 = [];
>>                     for (var j = 1; j <= 5; j += 1) {
>>                         collect10['push']([i, j]);
>>                     };
>>                     return collect10;
>>                 })());
>>             };
>>         };
>>         return append9;
>>     })();
>> };"
>>
>> What is the point of even having the 'with ({ i : i })' in there ??  I
>> have tried removing the form starting (ps-js:with ... ) and the code which
>> is then generated runs fine and has no 'with', but of course it is probably
>> breaking something else.  I don't understand why it's there.
>>
>> Regards,
>> Peter
>>
>> _______________________________________________
>> parenscript-devel mailing list
>> parenscript-devel at common-lisp.net
>> http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>>
>>
>
> _______________________________________________
> parenscript-devel mailing list
> parenscript-devel at common-lisp.net
> http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/parenscript-devel/attachments/20130302/925a0608/attachment.html>


More information about the parenscript-devel mailing list