Ok, here's a variation that abandons global MV altogether and stores it instead as a property on the caller. It passes all the previously mentioned cases and works at least some of the time with anonymous functions.<br>
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">(defpsmacro values (main &rest additional)</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (with-ps-gensyms (mv)</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> `(let ((,mv (list ,@additional)))</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (when (defined (@ (@ (@ arguments :callee) :caller) :mv))</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> (setf (@ (@ (@ arguments :callee) :caller) :mv) ,mv))</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (return ,main))))</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">(defpsmacro multiple-value-bind (vars expr &body body)</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (with-ps-gensyms (mv prev)</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> `(let ((,prev (@ (@ arguments :callee) :mv)))</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (try</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> (progn</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (setf (@ (@ arguments :callee) :mv) t)</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> (let ((,(car vars) ,expr)</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (,mv (if (objectp (@ (@ arguments :callee) :mv))</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> (@ (@ arguments :callee) :mv)</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (make-array ,(1- (length vars))))))</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> (destructuring-bind ,(cdr vars) ,mv</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> ,@body)))</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> (:finally (if (undefined ,prev)</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (delete (@ (@ arguments :callee) :mv))</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> (setf (@ (@ arguments :callee) :mv) ,prev)))))))</span><br><br>Specifically, it passes the case in Red's email that broke my previous attempt. That is, given ADD-TO-RESULT as defined in that earlier email,<br>
<br><span style="font-family: courier new,monospace;">(defun foo ()</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> (multiple-value-bind (a b) (add-to-result (lambda (x) (values 1 10)) 2)</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> (return (list a b))))</span><br><br>foo() now correctly evaluates to [3,undefined] instead of [3,10].<br><br>Can you guys come up with a new case to break it?<br><br>
Daniel<br><br><br><br><div class="gmail_quote">On Sun, Nov 1, 2009 at 1:41 PM, Red Daly <span dir="ltr"><<a href="mailto:reddaly@gmail.com">reddaly@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<br><br><div class="gmail_quote"><div class="im">On Sun, Nov 1, 2009 at 9:50 AM, 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="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
It might help to use a PS special variable and make MULTIPLE-VALUE-BIND responsible for cleanup.<br><br><span style="font-family: courier new,monospace;">(ps (defvar *mv* undefined)</span>)<br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">(defpsmacro values (main &rest additional)<br> (with-ps-gensyms (mv)<br> `(let ((,mv (list ,@additional)))<br> (when (defined *mv*)<br>
(setf *mv* ,mv))<br> (return ,main))))<br><br>(defpsmacro multiple-value-bind (vars expr &body body)<br> `(let ((*mv* '()))<br> (let ((,(car vars) ,expr))<br> (destructuring-bind ,(cdr vars) *mv*<br>
,@body))))<br></span><br>This works in the obvious cases. I'm not sure it handles Red's scenarios. Red, can you supply an example where this breaks?<br></blockquote></div><div><br><br>The main case I am concerned about is dealing with non-parenscript functions that will not manipulate the mv state. Something like the following would break the above code:<br>
<br>(defun foo ()<br> (let ((my-callback (lambda (x) (values 1 10)))<br> (multiple-value-bind (a b)<br> (add-to-result my-callback 2)))<br> <br>=><br><br>// imagine this is a non-Parenscript function that we cannot manipulate with the<br>
// Parenscript compiler<br>function addToResult(callback, x) { <br> // returns the result of adding x to the result of calling the callback<br> return callback() + x;<br>}<br><br>function foo () {<br> var myCallback = function (x) {<br>
if (MV !== undefined)<br>
MV = [10];<br>
return 1;<br>
};<br> <br> var old_MV = MV; // begin let<br> MV = null;<br> var a = addToResult(myCallback, 2);<br> var b = MV ? MV[0] : null;<br><br> // now b === 10 but it should be nil<br> // this is because addToResult did not reset MV<br>
<br> MV = old_MV; // end let<br> <br>}<br><br>This is why I think you might need to identify the callee in another special variable, and for emitted functions reset the MV_CALLEE variable before returning (and maybe in other places?).<br>
<font color="#888888">
<br><br>Red<br><br><br> </font></div><div><div></div><div class="h5"><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><font color="#888888"><br>
Daniel<br></font><br>p.s. I took the easy way out of making VALUES always prepend RETURN, but once Vladimir bestows implicit RETURN upon us we can take that out ;)<br>
<br><br><div class="gmail_quote"><div><div></div><div>On Sat, Oct 31, 2009 at 9:53 PM, Red Daly <span dir="ltr"><<a href="mailto:reddaly@gmail.com" target="_blank">reddaly@gmail.com</a>></span> wrote:<br>
</div></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div><div></div><div>I apologize for sending the first half of this email in error earlier:<br>
<br><div class="gmail_quote">
<div><div></div><div>On Sat, Oct 31, 2009 at 8:35 PM, Red Daly <span dir="ltr"><<a href="mailto:reddaly@gmail.com" target="_blank">reddaly@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">Hi Parenscripters,<br><br>As far as I can tell, multiple-value function calls are a unique feature of lisp. I would like the ability to perform multiple-value calls in Parenscript but I don't know if a sane solution exists or not.<br>
<br>Can anyone come up with a scheme for returning multiple values that translates well to Javascript? Ideally such a scheme would not introduce much overhead for usual functions that do not use or return multiple values (though perhaps setting some sort of global MV flag might be inexpensive enough). Functions that return multiple values should also only appear to return a single value when called by a function that expects only one return value (including native javascript functions).<br>
<br>(defun paren-mv-returner ()<br> (return (values 1 2 3)))<br><br>=><br><br><br>
function parenMvReturner() {<br>
/* do some magic with the values 2 and 3 */<br>
return 1; <br>
}<br><br>// one implementation might be<br>var mv = undefined;<br><br>function parenMvReturner() {<br>
mv = [2, 3];<br> return 1; <br>
}<br><br>// this scheme needs to adjust the return statement of every function so it might not be sufficient<br>// consider this other function<br><br>function parenMySingleReturner () {<br> var x = parenMvReturner();<br>
</blockquote></div></div><div>return x;<br>}<br><br>// parenMySingleReturner() will appear to return multiple values unless it modifies the mv value itself<br><br>// correction:<div><br>function parenMySingleReturner () {<br>
var x = parenMvReturner();<br></div>
mv = null;<br> return x;<br>
}<br> </div></div>But it seems like this solution will fall apart for calls to native Javascript functions over which we have no control. If we pass a multiple-value returning function as an argument to a native function, the native function will not perform the necessary mv-nulling when it returns.<br>
<br>someForeignJavascriptFunction( someMVReturningFunction)<br><br>will return whatever the someForeignJavascriptFunction should return, but it will also appear to return the other values that someMVReturningFunction set in the mv variable, since someForeignJavascriptFunction performs no cleanup.<br>
<br>Maybe this limitation can be avoided by having an mv-returning function A set a global variable "mvFunctionReturner" equal to the function A and a mv-receiver can check that mvFunctionReturner is set according to the function it called expecting multiple values. Does this scheme miss any cases?<br>
<br>Anyway I have thought a little bit about this and I thought I would pass it off to the rest of the Parenscripters as a thought experiment. Assume you can do a lot more semantic analysis than Parenscript currently does and transform the compiled source however you want. But any compiled functions must still be able to be treated as normal Javascript functions and all and only functions that should return multiple values appear to return them.<br>
<br>Cheers,<br><font color="#888888">Red<br>
</font><br></div></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://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel" target="_blank">http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel</a><br>
<br></div></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://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel" target="_blank">http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel</a><br>
<br></blockquote></div></div></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://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel" target="_blank">http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel</a><br>
<br></blockquote></div><br>