[parenscript-devel] A simpler way to do multiple value return? (Was: PS test failures in 7be9b45)

Red Daly reddaly at gmail.com
Wed Aug 29 02:46:02 UTC 2012


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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/parenscript-devel/attachments/20120828/c0c381c2/attachment.html>


More information about the parenscript-devel mailing list