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

Vladimir Sedach vsedach at gmail.com
Wed Aug 29 02:20:39 UTC 2012


The counter-example to using a global variable as I remember it from
the last discussion was this case:

(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?
>
> 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
>




More information about the parenscript-devel mailing list