[parenscript-devel] A simpler way to do multiple value return? (Was: PS test failures in 7be9b45)
Daniel Gackle
danielgackle at gmail.com
Wed Aug 29 03:52:29 UTC 2012
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
On Tue, Aug 28, 2012 at 8:46 PM, Red Daly <reddaly at gmail.com> wrote:
> On Tue, Aug 28, 2012 at 7:20 PM, Vladimir Sedach <vsedach at gmail.com>wrote:
>
>> 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.html
>
>>
>> (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 at 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 at 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 at 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 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
>> >
>> >
>> > 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 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
>>
>
>
> _______________________________________________
> 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/20120828/4eac13cd/attachment.html>
More information about the parenscript-devel
mailing list