<div dir="ltr"><div>Hi Daniel<br><br></div>You write: <br><br>"There's another problem with strict mode and PS: strict mode<div>banishes arguments.callee, which PS currently relies on for its</div><div>multiple-return-value implementation."<br>
<br></div><div>I hadn't been using multiple-value-bind, so I didn't notice. I have now experimented a bit and yet another problem is that it assigns to an undeclared variable __PS_MV_REG which is also not allowed in strict mode. In addition, it breaks if one uses a (funcall ...) in the value-form like:<br>
<br>(defun bar ()<br> (multiple-value-bind (x y)<br> (funcall foo 1 2))<br> (ps:array x y)) but that is not related <br><br></div><div>I am currently using a hacked version of parenscript with ps:*use-strict* which determines wether the 'with{}' stuff gets used by the compiler. I reluctantly decided I would have to, as I really need to 'use strict' and I ran into a problem with a complicated do* expression in which the compiler _also_ used 'with{}'.<br>
<br></div><div>With respect to the multiple return values implementation, could one not avoid the use of global variables by returning a js object, something like the following?<br><br></div><div>;;vals is like values<br>
</div><div>(ps:defpsmacro vals (&rest args)<br> (let ((idx-args (mapcan (lambda (idx arg)<br> (list idx arg))<br> (loop for i from 0 to (1- (length args))<br> collect i)<br>
args)))<br> `(ps:create ,@idx-args)))<br><br></div><div>;;mvb is like multiple-value-bind<br></div><div>(ps:defpsmacro mvb (vars vals-form &body body)<br> (let* ((val-form-name (ps:ps-gensym "vfn_"))<br>
(var-idxs (mapcar (lambda (var idx)<br> (list var `(ps:getprop ,val-form-name ,idx)))<br> vars<br> (loop for i from 0 to (1- (length vars))<br>
collect i))))<br> `(let* ((,val-form-name ,vals-form)<br> ,@var-idxs)<br> ,@body)))<br></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On 6 March 2013 21:46, Daniel Gackle <span dir="ltr"><<a href="mailto:danielgackle@gmail.com" target="_blank">danielgackle@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div>Hi Peter,</div><div><br></div><div>> Returning the closure from a function works the same in CL and</div><div>
> parenscript and does not generate the 'with'</div><div><br></div><div>Yes, because functions create new bindings for their args, which of</div>
<div>course suggests that PS might be able to use:</div><div><br></div><div> (function (i) { ... })(i)</div><div><br></div><div>instead of</div><div><br></div><div> with ({ i : i }) { ... }.</div><div><br></div><div>For example, evaluating the following in a PS hacked to</div>
<div>remove the 'with' trick produces the desired '(10 20 30):</div><div><br></div><div>(let ((closures (loop :for i :from 1 :to 3 :collect ((lambda (i) (lambda () (* i 10))) i))))</div><div> (loop :for fn :in closures :collect (funcall fn)))</div>
<div><br></div><div>Would that trick work correctly everywhere that the 'with' trick does?</div><div>I'm not sure if one might run into trouble with statements vs.</div><div>expressions in some cases, but the PS implementation has solved most</div>
<div>of those issues, so I'd be surprised if lambdas couldn't do it. You'd</div><div>arguably take a hit in readability, but might gain something in</div><div>performance, since 'with' is slow (or used to be). It's also worth</div>
<div>noting that since the 'with' trick is there only for closures that</div><div>capture loop vars, it needn't be the whole loop body that gets wrapped</div><div>in an extra lambda (the way it currently gets wrapped in 'with'), but</div>
<div>only the creation of the closure itself — which is certainly just an</div><div>expression.</div><div><br></div><div>Hopefully Vladimir will eventually chime in on this (he's been busy</div><div>working in LA lately, but we expect he'll get back to open source</div>
<div>at some point.)</div><div><br></div><div>There's another problem with strict mode and PS: strict mode</div><div>banishes arguments.callee, which PS currently relies on for its</div><div>multiple-return-value implementation.</div>
<div><br></div><div>I'm glad you raised this question. It seems like PS ought to be able</div><div>to emit strict code on demand, if not by default. One of PS's goals</div><div>has always been to produce efficient JS, and one of the reasons for</div>
<div>strict mode is to support optimization. Moreover, the annoyance of</div><div>having to declare strict mode in JS scripts or functions is alleviated</div><div>by having a compiler like PS do it for you. A nice touch would be to</div>
<div>leverage the DECLARE syntax to get this at the function level.</div><div><br></div></div><div>Daniel</div><div><br></div><br><div class="gmail_quote">On Sat, Mar 2, 2013 at 1:47 AM, Peter Wood <span dir="ltr"><<a href="mailto:p.r.wood@gmail.com" target="_blank">p.r.wood@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><div>Hi Daniel<br><br><br></div>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:<div>
<br>
<br> (let ((closures (loop :for i :from 1 :to 3 <br></div> :collect (let* ((j i)<br> (cl (lambda () (* j 10))))<br> cl))))<div>
<br> (loop :for fn :in closures :collect (funcall fn)))<br>
<br></div>==> (10 20 30)<br><br></div><div>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.<br>
<br>(defun foo ()<br> (labels ((make-cl (i) (lambda () (* i 10))))<div><br> (let ((closures (loop :for i :from 1 :to 3<br></div> :collect (make-cl i))))<br>
(loop :for fn :in closures :collect (funcall fn)))))<br>
<br></div><div>(foo) ==> (10 20 30)<br><br></div><div>foo() ==> [10, 20, 30]<br><br></div><div>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!<br>
<br></div><div>Regards,<br></div><div>Peter<br></div><div><br></div><br><div><br></div></div><div><div><div class="gmail_extra"><br><br><div class="gmail_quote">On 2 March 2013 02:08, Daniel Gackle <span dir="ltr"><<a href="mailto:danielgackle@gmail.com" target="_blank">danielgackle@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div>Hi Peter,</div><div><br></div><div>Welcome! The 'with' trick (which confused me when I first saw it as</div>
<div>well) occurs when a loop contains a closure that captures the loop</div><div>iteration variable:</div>
<div><br></div><div> (loop :for i :from 1 :to 3 :collect (lambda () (* i 10)))</div><div><br></div><div>The question is what value of i each lambda should use when it's</div><div>called. The 'with' trick establishes a new scope with a new binding</div>
<div>for 'i' inside the loop body and puts the closure inside that scope.</div><div>Thus, if you do this:</div><div><br></div><div> (let ((closures (loop :for i :from 1 :to 3 :collect (lambda () (* i 10)))))</div>
<div> (loop :for fn :in closures :collect (funcall fn)))</div><div><br></div><div>... you get '(10 20 30), because each closure remembers the value that</div><div>i had when it was created. Without the 'with' trick, you'd get '(40 40 40),</div>
<div>because the closures all share the loop's original binding for i, and</div><div>that held 40 by the time the loop terminated. </div><div><br></div><div>The fact that it breaks Strict mode, though, means that either PS's</div>
<div>implementation should change, at least to offer the option of not</div><div>using it, or drop the trick altogether. I have a feeling the latter</div><div>would be simplest. For one thing, Common Lisp, which is PS's</div>
<div>touchstone, doesn't have this scoping behavior. In CCL I get '(40 40 40)</div><div>for the above expression. And DOTIMES is the same:</div><div><br></div><div> (let ((list nil))</div><div> (dotimes (i 3) (push (lambda () (* i 10)) list))</div>
<div> (mapcar #'funcall (reverse list)))</div><div><br></div><div>=> (30 30 30)</div><div><br></div><div>So this is a case of plus royaliste que le roi that could arguably just be</div><div>abandoned. If not, though, a special variable for Strict Mode would be</div>
<div>a good idea. Vladimir?</div><div><br></div><div>Daniel</div><div><br></div><br><div class="gmail_quote"><div><div>On Fri, Mar 1, 2013 at 12:39 PM, Peter Wood <span dir="ltr"><<a href="mailto:p.r.wood@gmail.com" target="_blank">p.r.wood@gmail.com</a>></span> wrote:<br>
</div></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div><div dir="ltr"><div><div><div><div><div><div><div>Hi<br><br></div>It's my first post, so first of all, thanks to everyone who works on parenscript. It is a lifesaver.<br>
<br></div>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:<br>
<br><a href="https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode" target="_blank">https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode</a><br>
<br>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)<br>
<br></div>(defun compile-loop-body <br>...<br>(aif (sort (remove-duplicates *loop-scope-lexicals-captured*)<br> #'string< :key #'symbol-name)<br> `(ps-js:block<br> (ps-js:with<br>
,(compile-expression<br> `(create<br> ,@(loop for x in it<br> collect x<br> collect (when (member x loop-vars) x))))<br>
,compiled-body))<br> compiled-body)))<br><br></div>Here is an example of some lisp and the js which it generates:<br><br>(ps:ps (defun foo ()<br> (loop for i from 1 to 5<br> append (loop for j from 1 to 5<br>
collect (list i j))))) <br>==><br>"function foo() {<br> return (function () {<br> var append9 = [];<br> for (var i = 1; i <= 5; i += 1) {<br> with ({ i : i }) {<br>
^^^^^^^^^^<br> append9 = append9.concat((function () {<br> var collect10 = [];<br> for (var j = 1; j <= 5; j += 1) {<br> collect10['push']([i, j]);<br>
};<br> return collect10;<br> })());<br> };<br> };<br> return append9;<br> })();<br>};"<br><br></div>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.<br>
<br></div>Regards,<br></div>Peter<br></div>
<br></div></div>_______________________________________________<br>
parenscript-devel mailing list<br>
<a href="mailto:parenscript-devel@common-lisp.net" target="_blank">parenscript-devel@common-lisp.net</a><br>
<a href="http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel" target="_blank">http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel</a><br>
<br></blockquote></div><br>
<br>_______________________________________________<br>
parenscript-devel mailing list<br>
<a href="mailto:parenscript-devel@common-lisp.net" target="_blank">parenscript-devel@common-lisp.net</a><br>
<a href="http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel" target="_blank">http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel</a><br>
<br></blockquote></div><br></div>
</div></div><br>_______________________________________________<br>
parenscript-devel mailing list<br>
<a href="mailto:parenscript-devel@common-lisp.net" target="_blank">parenscript-devel@common-lisp.net</a><br>
<a href="http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel" target="_blank">http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel</a><br>
<br></blockquote></div><br>
<br>_______________________________________________<br>
parenscript-devel mailing list<br>
<a href="mailto:parenscript-devel@common-lisp.net">parenscript-devel@common-lisp.net</a><br>
<a href="http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel" target="_blank">http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel</a><br>
<br></blockquote></div><br></div>