<div>First, your comment "they all need to be stack aware" reminds me of a</div><div>point I forgot to make about the global variable idea. Obviously a</div><div>naive global RETURN_VALUES array isn't going to survive cases of</div>
<div>nested MV calls. But could the compiled PS instead keep a global</div><div>RETURN_VALUES stack? i.e. a list of MV arrays that callers would</div><div>push/pop as appropriate? I haven't thought about how this might work</div>
<div>and it feels like it probably wouldn't, but I'd like to know why.</div><div><br></div><div>< The basic idea is that a form expecting multiple values creates a</div><div>mutable array to store those values, and passes it down the stack ></div>
<div><br></div><div>I like the array-passing idea and agree that it probably needs to be</div><div>passed out of band - but can we state explicitly why? For example, why</div><div>can't we make it a hidden first argument (it couldn't very well go</div>
<div>anywhere else because of things like &REST) and make Parenscript smart</div><div>enough to add in the correct value for that hidden argument every</div><div>place that function is called (i.e. pass null if the extra return</div>
<div>values aren't to be bound, and an array to hold them if they are)?</div><div><br></div><div>One obvious drawback is that non-PS functions wouldn't be able to call</div><div>such a function normally; they'd have to know about the extra arg.</div>
<div>What other drawbacks are there?</div><div><br></div><div>< Note that we just need some way to associate foo with the array. You</div><div>can do that with a global table instead of setting a property on the</div>
<div>
function object. ></div><div><br></div><div>I like this idea, because everyone in the JS world is so adamant that</div><div>one shouldn't mess with arguments.metablah (though arguments.callee</div><div>has got to be better than arguments.callee.caller). But how would it</div>
<div>work for lambdas?</div><div><br></div><div>< A problem arises for recursive [including mutually recursive] functions ></div><div><br></div><div>Right. I don't follow your example here, though, so I wonder if you</div>
<div>can spell it out a bit further.</div><div><br></div><div>Daniel</div><br><div class="gmail_quote">On Thu, Aug 30, 2012 at 7:12 PM, Vladimir Sedach <span dir="ltr"><<a href="mailto:vsedach@gmail.com" target="_blank">vsedach@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">I played around with several approaches to multiple values, and Red is<br>
correct that they all need to be stack aware. However, it turns out<br>
you don't need callee.caller for that, just first-class functions and<br>
closures. If you don't care about changing the calling convention,<br>
you don't even need that.<br>
<br>
The basic idea is that a form expecting multiple values creates a<br>
mutable array to store those values, and passes it down the stack<br>
(this is a common pattern in C code). If you don't care about function<br>
calling convention, you can just pass around a mutable array for<br>
multiple values as an implicit part of the argument list.<br>
<br>
Parenscript cares, so we need to pass that data out-of-band. We can do<br>
this by associating the function object about to be called with the<br>
multiple value array:<br>
<br>
(multiple-value-bind (x y) (foo)<br>
...body...)<br>
<br>
prev_mv = <a href="http://foo.mv" target="_blank">foo.mv</a>; // don't clobber things up the stack<br>
var values = [];<br>
<a href="http://foo.mv" target="_blank">foo.mv</a> = values;<br>
x = foo();<br>
if (values.length > 0) {<br>
y = values[0];<br>
}<br>
<a href="http://foo.mv" target="_blank">foo.mv</a> = prev_mv;<br>
...body...<br>
<br>
Note that we just need some way to associate foo with the array. You<br>
can do that with a global table instead of setting a property on the<br>
function object.<br>
<br>
Functions that return multiple values look like:<br>
<br>
(defun foo (x y z)<br>
(values x y z))<br>
<br>
function foo (x, y, z) {<br>
var values = <a href="http://arguments.callee.mv" target="_blank">arguments.callee.mv</a>;<br>
if (values) {<br>
values[0] = y;<br>
values[1] = z;<br>
}<br>
return x;<br>
}<br>
<br>
Note that you don't need arguments.callee for a function to have a<br>
reference to itself:<br>
<br>
var foo = (function () {<br>
var self = function foo () {<br>
self.blah = whatever;<br>
...body...<br>
};<br>
return self;<br>
})();<br>
<br>
That is very ugly though.<br>
<br>
multiple-value "pass-through" only happens in the case when there is<br>
an expression like "(return (some-multi-valued-function))" in the<br>
code. Since Parenscript now instruments all returns (this was not the<br>
case when the original multiple value mechanism was worked out), we<br>
can pass multiple values along like so:<br>
<br>
(defun bar ()<br>
(foo)<br>
(foo))<br>
<br>
function bar () {<br>
foo(); // first invocation, don't care about multiple values<br>
<a href="http://foo.mv" target="_blank">foo.mv</a> = <a href="http://arguments.callee.mv" target="_blank">arguments.callee.mv</a>;<br>
var result = foo();<br>
delete <a href="http://foo.mv" target="_blank">foo.mv</a>;<br>
return result;<br>
}<br>
<br>
As you can see we only give the array to functions in instances where<br>
they can potentially return multiple values.<br>
<br>
A problem arises for recursive functions (and any function objects<br>
that can appear multiple times in the stack):<br>
<br>
(defun foo (x)<br>
(if (= x 1)<br>
(values 1 2)<br>
(1+ (foo (1- x)))))<br>
<br>
foo(2) will now return multiple values, even though it shouldn't.<br>
<br>
In general, this can happen if foo calls any function x calls... a<br>
function that eventually calls foo again.<br>
<br>
This wouldn't happen if the values array was passed as an argument.<br>
<br>
One way I see to solve this problem is to add some code to any<br>
function that can potentially return multiple values:<br>
<br>
function foo (x) {<br>
var values = <a href="http://arguments.callee.mv" target="_blank">arguments.callee.mv</a>;<br>
delete <a href="http://arguments.callee.mv" target="_blank">arguments.callee.mv</a>;<br>
<br>
if (x === 1) {<br>
if (values) values[0] = 2;<br>
return 1;<br>
} else {<br>
return 1 + foo(x - 1); // not expecting values<br>
}<br>
}<br>
<br>
Obviously the above code will need things like unwind-protect and<br>
gensyms, etc., but does anyone see anything that's wrong with the<br>
above proposal?<br>
<span class="HOEnZb"><font color="#888888"><br>
Vladimir<br>
</font></span><div class="HOEnZb"><div class="h5"><br>
<br>
On Wed, Aug 29, 2012 at 1:33 PM, Daniel Gackle <<a href="mailto:danielgackle@gmail.com">danielgackle@gmail.com</a>> wrote:<br>
> Ah yes clearer. This is similar to Vladimir's case from upthread:<br>
><br>
> (defun foo ()<br>
> (blah)<br>
> (some-random-js-function))<br>
><br>
> ... except that my suggestion that the compiler figure out when BLAH<br>
> isn't in a return position and do something like:<br>
><br>
> function foo() {<br>
> blah();<br>
> RETURN_VALUES = null;<br>
> return someRandomJsFunction();<br>
> };<br>
><br>
> ... won't work in your case, i.e. when FOO is not a PS function.<br>
><br>
> But I wonder whether perfect interop from JS back into PS isn't an<br>
> overly ambitious a thing to promise. When language A and language B<br>
> have different calling conventions and you call B from A, it's normal<br>
> to have to follow some protocol to manually bridge the gap between<br>
> them. In this case the protocol might be: you must either return the<br>
> call to BLAH or clear RETURN_VALUES. Yeah this would be a pain and<br>
> easy to forget, but it is arguably a reasonable card for PS to have to<br>
> play here.<br>
><br>
> That being said, it's not surprising that a simple global variable<br>
> wouldn't do the trick in every case. I hope we can come up with<br>
> something that does.<br>
><br>
> But let's not forget that the current implementation is even more<br>
> broken. It doesn't do the right thing even if BLAH is in a return<br>
> position - so *none* of the cases we're talking about actually work<br>
> right now.<br>
><br>
> Daniel<br>
><br>
><br>
> On Wed, Aug 29, 2012 at 10:16 AM, Red Daly <<a href="mailto:reddaly@gmail.com">reddaly@gmail.com</a>> wrote:<br>
>><br>
>> Hi Daniel,<br>
>><br>
>> I'm glad to be part of the discussion :)<br>
>><br>
>> On Aug 28, 2012 8:53 PM, "Daniel Gackle" <<a href="mailto:danielgackle@gmail.com">danielgackle@gmail.com</a>> wrote:<br>
>>><br>
>>> Hi Red,<br>
>>><br>
>>> I was hoping you'd chime in. I'll see your scenario 3 and raise you a<br>
>>> 3a and a 3b:<br>
>>><br>
>>> Scenario 3a: A non-Parenscript function that calls a mv-returning<br>
>>> Parenscript function but only needs its first return value;<br>
>>><br>
>>> Scenario 3b: A non-Parenscript function that calls a mv-returning<br>
>>> Parenscript function and needs all its return values.<br>
>>><br>
>>> 3a works fine as long as the MV implementation is careful to use a<br>
>>> normal JS return to pass the first return value back to the caller.<br>
>>> That's true both of what PS does today and of the global-var proposal.<br>
>>><br>
>>> As for 3b (the scenario, not the hacker!), seems to me this can't work<br>
>>> at all and there's no need to support it. If you're a non-PS function<br>
>>> then by definition you can't use PS's MULTIPLE-VALUE-BIND to access<br>
>>> the additional return values because the MV construct only exists in<br>
>>> PS. I suppose if you really wanted to you could manually write JS to<br>
>>> do whatever PS does to supply those values to the caller, but then<br>
>>> you're not really a non-PS function anymore, so much as a<br>
>>> manually-compiled PS function.<br>
>>><br>
>>> Daniel<br>
>><br>
>><br>
>> I wasn't clear in my original post. I'm not concerned with the<br>
>> non-Parenscript function's ability to receive multiple values. I'm<br>
>> concerned that the non-Parenscript function will interfere with the multiple<br>
>> value return.<br>
>><br>
>> If a non-Parenscript function calls a Parenscript function that messes<br>
>> with the global variables, the non-Parenscript function could end up<br>
>> returning stuff unintentionally. This is because the non-Parenscript<br>
>> function will not manipulate the global variables to clean up the unused<br>
>> multiple values that are returned.<br>
>><br>
>> Here's some code to illustrait the point:<br>
>><br>
>> (defun ps-foo ()<br>
>> (multiple-value-bind (a b) (bar)<br>
>> (+ a b)))<br>
>><br>
>> (defun ps-fn-returns-mv ()<br>
>> (values 1 2))<br>
>><br>
>> function barWorks() {<br>
>> return psFnReturnsMv();<br>
>> }<br>
>><br>
>> function barBreaks() {<br>
>> psFnReturnsMv(); // global variables for MV returning get set up and<br>
>> linger<br>
>> // return a single value: 42<br>
>> return 42;<br>
>> }<br>
>><br>
>> Let's assume the two Parenscript functions translate into something like<br>
>> this:<br>
>><br>
>> var RETURN_VALUES = null;<br>
>> var MV_CALL = false;<br>
>> ... other global variables related to multiple values<br>
>><br>
>> function psFoo() {<br>
>> // global variable stuff<br>
>> MV_CALL = true;<br>
>> RETURN_VALUES = null;<br>
>><br>
>> var a = bar();<br>
>> MV_CALL = false;<br>
>> var b = RETURN_VALUES ? RETURN_VALUES[0] : undefined;<br>
>> return a + b;<br>
>> }<br>
>><br>
>> function psFnReturnsMv() {<br>
>> if (MV_CALL)<br>
>> RETURN_VALUES = [ 2 ];<br>
>> return 1;<br>
>> }<br>
>><br>
>><br>
>> When bar = barWorks, foo will return 1 + 2, as intended. When bar =<br>
>> barBreaks, foo will incorrectly return 42 + 2 because the global variables<br>
>> were not properly cleaned up.<br>
>><br>
>> I hope this makes sense.<br>
>><br>
>> - Red<br>
>><br>
><br>
><br>
</div></div><div class="HOEnZb"><div class="h5">> _______________________________________________<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>
<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>
</div></div></blockquote></div><br>