Hi Daniel,
I'm glad to be part of the discussion :) On Aug 28, 2012 8:53 PM, "Daniel Gackle" danielgackle@gmail.com wrote:
Hi Red,
I was hoping you'd chime in. I'll see your scenario 3 and raise you a 3a and a 3b:
Scenario 3a: A non-Parenscript function that calls a mv-returning Parenscript function but only needs its first return value;
Scenario 3b: A non-Parenscript function that calls a mv-returning Parenscript function and needs all its return values.
3a works fine as long as the MV implementation is careful to use a normal JS return to pass the first return value back to the caller. That's true both of what PS does today and of the global-var proposal.
As for 3b (the scenario, not the hacker!), seems to me this can't work at all and there's no need to support it. If you're a non-PS function then by definition you can't use PS's MULTIPLE-VALUE-BIND to access the additional return values because the MV construct only exists in PS. I suppose if you really wanted to you could manually write JS to do whatever PS does to supply those values to the caller, but then you're not really a non-PS function anymore, so much as a manually-compiled PS function.
Daniel
I wasn't clear in my original post. I'm not concerned with the non-Parenscript function's ability to receive multiple values. I'm concerned that the non-Parenscript function will interfere with the multiple value return.
If a non-Parenscript function calls a Parenscript function that messes with the global variables, the non-Parenscript function could end up returning stuff unintentionally. This is because the non-Parenscript function will not manipulate the global variables to clean up the unused multiple values that are returned.
Here's some code to illustrait the point:
(defun ps-foo () (multiple-value-bind (a b) (bar) (+ a b)))
(defun ps-fn-returns-mv () (values 1 2))
function barWorks() { return psFnReturnsMv(); }
function barBreaks() { psFnReturnsMv(); // global variables for MV returning get set up and linger // return a single value: 42 return 42; }
Let's assume the two Parenscript functions translate into something like this:
var RETURN_VALUES = null; var MV_CALL = false; ... other global variables related to multiple values
function psFoo() { // global variable stuff MV_CALL = true; RETURN_VALUES = null;
var a = bar(); MV_CALL = false; var b = RETURN_VALUES ? RETURN_VALUES[0] : undefined; return a + b; }
function psFnReturnsMv() { if (MV_CALL) RETURN_VALUES = [ 2 ]; return 1; }
When bar = barWorks, foo will return 1 + 2, as intended. When bar = barBreaks, foo will incorrectly return 42 + 2 because the global variables were not properly cleaned up.
I hope this makes sense.
- Red
On Tue, Aug 28, 2012 at 8:46 PM, Red Daly reddaly@gmail.com wrote:
On Tue, Aug 28, 2012 at 7:20 PM, Vladimir Sedach vsedach@gmail.comwrote:
The counter-example to using a global variable as I remember it from the last discussion was this case:
The last discussion: http://lists.common-lisp.net/pipermail/parenscript-devel/2009-October/000639...
(defun blah () (values 1 2 3))
(defun foo () (blah) (some-random-js-function))
(defun bar () (multiple-value-bind (a b c) (foo) (+ a b c)))
Now a call to bar returns 6, when it shouldn't. I think it might be possible to use another global variable as a flag that's set and checked by multiple-value aware PS functions, but I need to think about how to make it work.
Vladimir
On Tue, Aug 28, 2012 at 10:07 PM, Daniel Gackle danielgackle@gmail.com wrote:
Those test failures make sense now - thanks. Also, this comment reminded me of something:
< the callee.caller property for functions, which multiple value return depends on >
I dislike our implementation for multiple value return. Stuffing the values in callee.caller is complicated and doesn't feel right (I think I may have been the one who came up with it; it was a bad idea), plus it relies on one of the shakiest aspects if not of JS itself than certainly of the JS implementations.
A simpler way occurred to me the other day and I'd like to know where it breaks. The argument goes like this: since JS is single-threaded and functions have to return synchronously, there can be only one function return in play at any given time, therefore there can be only one multiple-return-value list at any time, therefore why not just store it in a global variable?
My guess at why your proposal won't work: It ignores knowledge of the call stack, which is practically necessary.
Imagine a form (multiple-values-bind (a b c ...) (FOO ...)). Any solution of this with global variables will take the form:
step 1: Do some stuff with global variable manipulation step 2: Call FOO step 3: Do some stuff to clean up.
Pretty much any code can execute inside FOO. This includes a few scenarios:
Scenario 1: A simple JS function, native or not, that doesn't call anything that returns multiple values. Your solution works. Scenario 2: A Parenscript function, written to manipulate your global variables properly. Your solution works. Scenario 3: A non-Parenscript function that calls a mv-returning Parenscript function.
Can your solution handle Scenario 3? My guess is it cannot because it ignores the call stack.
Just my quick guess.
- Red
Say this variable is called *spillover*. Then this:
(defun blah () (values 1 2 3))
(defun callblah () (multiple-value-bind (a b c) (blah) (+ a b c)))
...might compile to:
function blah() { SPILLOVER = [2, 3]; return 1; }; function callblah() { var a = blah(); var b = SPILLOVER[1]; var c = SPILLOVER[2]; return a + b + c; };
There might be complicating factors that would make the JS more involved in practice, but I don't remember what they are.
Apart from being so much simpler, this implementation has two advantages. First, it's "morally" better in Eugenia Cheng's sense (http://cheng.staff.shef.ac.uk/morality/morality.pdf). The multiple return values don't belong to caller or callee, but to the call itself. Caller and callee are functions that persist across many calls and ought not to have information about a specific call attached to them. That's why PS is forced to add ugly code to save the previous value attached to callee and restore it using a try/finally at the end.
Second, it would fix a known problem. PS breaks when you have an interloper like FOO here:
(defun blah () (values 1 2 3))
(defun foo () (blah))
(defun bar () (multiple-value-bind (a b c) (foo) (+ a b c)))
BAR should return 6, and does in CL, but in PS it returns 1, because FOO doesn't return multiple values, so B and C are null and "1 + null + null" is 1 in JS. But the *spillover* hack would make BAR return 6.
Could we get away with this, or what am I missing?
Daniel
On Tue, Aug 28, 2012 at 6:00 PM, Vladimir Sedach vsedach@gmail.com
wrote:
Hi Daniel,
Yes, those two failures in the eval test suite are expected. CL-JavaScript doesn't have the callee.caller property for functions, which multiple value return depends on. I wasn't sure where to comment those tests out, so I left them in to remind myself to add callee.caller to CL-JavaScript (I've already talked to Marijn Haverbeke about that).
Thank you, Vladimir
On Mon, Aug 27, 2012 at 11:58 PM, Daniel Gackle <
danielgackle@gmail.com>
wrote:
I've rebased my PS LOOP extensions [1] onto the latest commit (7be9b45) and recompiled Skysheet. The generated JS looks fine.
There
was one glitch that I'll report separately along with a workaround. Before pushing the LOOP extensions onto master, though, I want to update any relevant PS tests. Some will fail because the LOOP output has changed quite a bit. Unfortunately I'm also seeing failures
when I
run the tests in 7be9b45, which is prior to any of these LOOP changes. I've pasted the output below [2]. It doesn't look like
these
failures are related to work in ps-loop.lisp, so I'll just ignore
them
for the time being, but Vladimir can you please comment on whether
you
know about them or whether there's something unexpected going on?
Daniel
[1] These are the constructs FOR..OF and MAP..TO, plus a change to FOR..ON, that I described in my email to this list on April 11.
They
are currently sitting in the "loop" branch. Rebasing them was nontrivial because of Boris' additions to ps-loop.lisp, but it seems to have all gone ok. Boris, if you're reading this, please look out
for
any regressions once I push these changes and let us know if you notice anything.
[2] Running output tests:
........................................................................................................................................................................................................................................................................................................................................................................................................................................
Did 424 checks. Pass: 424 (100%) Skip: 0 ( 0%) Fail: 0 ( 0%) Running package system tests: ......... Did 9 checks. Pass: 9 (100%) Skip: 0 ( 0%) Fail: 0 ( 0%) Running CL-JavaScript eval tests: ...........................f...............X...................... Did 66 checks. Pass: 64 (96%) Skip: 0 ( 0%) Fail: 2 ( 3%) Failure Details:
mv-return1 []: Unexpected Error: #<cl-js:js-condition #x30200155257D> [js] TypeError: undefined has no properties...
dynamic-extent-function-return-values []: (funcall (if (typep #:g36204 'structure-object) #'equalp
#'equal)
#:g36204 (jsarray '(1 2 3))) was NIL..
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
Those test failures make sense now - thanks. Also, this comment reminded me of something:
< the callee.caller property for functions, which multiple value return depends on >
I dislike our implementation for multiple value return. Stuffing the values in callee.caller is complicated and doesn't feel right (I think I may have been the one who came up with it; it was a bad idea), plus it relies on one of the shakiest aspects if not of JS itself than certainly of the JS implementations.
A simpler way occurred to me the other day and I'd like to know where it breaks. The argument goes like this: since JS is single-threaded and functions have to return synchronously, there can be only one function return in play at any given time, therefore there can be only one multiple-return-value list at any time, therefore why not just store it in a global variable?
Say this variable is called *spillover*. Then this:
(defun blah () (values 1 2 3))
(defun callblah () (multiple-value-bind (a b c) (blah) (+ a b c)))
...might compile to:
function blah() { SPILLOVER = [2, 3]; return 1; }; function callblah() { var a = blah(); var b = SPILLOVER[1]; var c = SPILLOVER[2]; return a + b + c; };
There might be complicating factors that would make the JS more involved in practice, but I don't remember what they are.
Apart from being so much simpler, this implementation has two advantages. First, it's "morally" better in Eugenia Cheng's sense (http://cheng.staff.shef.ac.uk/morality/morality.pdf). The multiple return values don't belong to caller or callee, but to the call itself. Caller and callee are functions that persist across many calls and ought not to have information about a specific call attached to them. That's why PS is forced to add ugly code to save the previous value and restore it using a try/finally at the end.
Second, it would fix a known problem. PS breaks when you introduce an interloper like FOO here:
(defun blah () (values 1 2 3))
(defun foo () (blah))
(defun bar () (multiple-value-bind (a b c) (foo) (+ a b c)))
BAR should return 6, and does in CL, but in PS it returns 1, because FOO doesn't return multiple values, so B and C are null and "1 + null + null" is 1 in JS. But the *spillover* hack would make BAR return 6.
Could we get away with this, or what am I missing?
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 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