[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 02:07:14 UTC 2012


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


More information about the parenscript-devel mailing list