[parenscript-devel] Multiple value calls

Daniel Gackle danielgackle at gmail.com
Mon Nov 2 00:34:13 UTC 2009


Ok, here's a variation that abandons global MV altogether and stores it
instead as a property on the caller. It passes all the previously mentioned
cases and works at least some of the time with anonymous functions.

(defpsmacro values (main &rest additional)
  (with-ps-gensyms (mv)
    `(let ((,mv (list , at additional)))
       (when (defined (@ (@ (@ arguments :callee) :caller) :mv))
         (setf (@ (@ (@ arguments :callee) :caller) :mv) ,mv))
       (return ,main))))

(defpsmacro multiple-value-bind (vars expr &body body)
  (with-ps-gensyms (mv prev)
    `(let ((,prev (@ (@ arguments :callee) :mv)))
       (try
        (progn
          (setf (@ (@ arguments :callee) :mv) t)
          (let ((,(car vars) ,expr)
                (,mv (if (objectp (@ (@ arguments :callee) :mv))
                         (@ (@ arguments :callee) :mv)
                         (make-array ,(1- (length vars))))))
            (destructuring-bind ,(cdr vars) ,mv
              , at body)))
        (:finally (if (undefined ,prev)
                      (delete (@ (@ arguments :callee) :mv))
                      (setf (@ (@ arguments :callee) :mv) ,prev)))))))

Specifically, it passes the case in Red's email that broke my previous
attempt. That is, given ADD-TO-RESULT as defined in that earlier email,

(defun foo ()
    (multiple-value-bind (a b) (add-to-result (lambda (x) (values 1 10)) 2)
      (return (list a b))))

foo() now correctly evaluates to [3,undefined] instead of [3,10].

Can you guys come up with a new case to break it?

Daniel



On Sun, Nov 1, 2009 at 1:41 PM, Red Daly <reddaly at gmail.com> wrote:

>
>
> On Sun, Nov 1, 2009 at 9:50 AM, Daniel Gackle <danielgackle at gmail.com>wrote:
>
>> It might help to use a PS special variable and make MULTIPLE-VALUE-BIND
>> responsible for cleanup.
>>
>> (ps (defvar *mv* undefined))
>>
>> (defpsmacro values (main &rest additional)
>>   (with-ps-gensyms (mv)
>>     `(let ((,mv (list , at additional)))
>>        (when (defined *mv*)
>>          (setf *mv* ,mv))
>>        (return ,main))))
>>
>> (defpsmacro multiple-value-bind (vars expr &body body)
>>   `(let ((*mv* '()))
>>      (let ((,(car vars) ,expr))
>>       (destructuring-bind ,(cdr vars) *mv*
>>         , at body))))
>>
>> This works in the obvious cases. I'm not sure it handles Red's scenarios.
>> Red, can you supply an example where this breaks?
>>
>
>
> The main case I am concerned about is dealing with non-parenscript
> functions that will not manipulate the mv state.  Something like the
> following would break the above code:
>
> (defun foo ()
>    (let ((my-callback (lambda (x) (values 1 10)))
>    (multiple-value-bind (a b)
>        (add-to-result my-callback 2)))
>
> =>
>
> // imagine this is a non-Parenscript function that we cannot manipulate
> with the
> // Parenscript compiler
> function addToResult(callback, x) {
>    // returns the result of adding x to the result of calling the callback
>    return callback() + x;
> }
>
> function foo () {
>    var myCallback = function (x) {
>    if (MV !== undefined)
>       MV = [10];
>    return 1;
> };
>
>    var old_MV = MV; // begin let
>    MV = null;
>    var a = addToResult(myCallback, 2);
>    var b = MV ? MV[0] : null;
>
>    // now b === 10 but it should be nil
>    // this is because addToResult did not reset MV
>
>    MV = old_MV; // end let
>
> }
>
> This is why I think you might need to identify the callee  in another
> special variable, and for emitted functions reset the MV_CALLEE variable
> before returning (and maybe in other places?).
>
>
> Red
>
>
>
>
>>
>> Daniel
>>
>> p.s. I took the easy way out of making VALUES always prepend RETURN, but
>> once Vladimir bestows implicit RETURN upon us we can take that out ;)
>>
>>
>> On Sat, Oct 31, 2009 at 9:53 PM, Red Daly <reddaly at gmail.com> wrote:
>>
>>> I apologize for sending the first half of this email in error earlier:
>>>
>>> On Sat, Oct 31, 2009 at 8:35 PM, Red Daly <reddaly at gmail.com> wrote:
>>>
>>>> Hi Parenscripters,
>>>>
>>>> As far as I can tell, multiple-value function calls are a unique feature
>>>> of lisp.  I would like the ability to perform multiple-value calls in
>>>> Parenscript but I don't know if a sane solution exists or not.
>>>>
>>>> Can anyone come up with a scheme for returning multiple values that
>>>> translates well to Javascript?  Ideally such a scheme would not introduce
>>>> much overhead for usual functions that do not use or return multiple values
>>>> (though perhaps setting some sort of global MV flag might be inexpensive
>>>> enough).  Functions that return multiple values should also only appear to
>>>> return a single value when called by a function that expects only one return
>>>> value (including native javascript functions).
>>>>
>>>> (defun paren-mv-returner ()
>>>>   (return (values 1 2 3)))
>>>>
>>>> =>
>>>>
>>>>
>>>> function parenMvReturner() {
>>>>    /* do some magic with the values 2 and 3 */
>>>>    return 1;
>>>> }
>>>>
>>>> // one  implementation might be
>>>> var mv = undefined;
>>>>
>>>> function parenMvReturner() {
>>>>    mv = [2, 3];
>>>>    return 1;
>>>> }
>>>>
>>>> // this scheme needs to adjust the return statement of every function so
>>>> it might not be sufficient
>>>> // consider this other function
>>>>
>>>> function parenMySingleReturner () {
>>>>    var x = parenMvReturner();
>>>>
>>> return x;
>>> }
>>>
>>> // parenMySingleReturner() will appear to return multiple values unless
>>> it modifies the mv value itself
>>>
>>> // correction:
>>>
>>> function parenMySingleReturner () {
>>>    var x = parenMvReturner();
>>>    mv = null;
>>>    return x;
>>> }
>>>
>>> But it seems like this solution will fall apart for calls to native
>>> Javascript functions over which we have no control.  If we pass a
>>> multiple-value returning function as an argument to a native function, the
>>> native function will not perform the necessary mv-nulling when it returns.
>>>
>>> someForeignJavascriptFunction( someMVReturningFunction)
>>>
>>> will return whatever the someForeignJavascriptFunction should return, but
>>> it will also appear to return the other values that someMVReturningFunction
>>> set in the mv variable, since someForeignJavascriptFunction performs no
>>> cleanup.
>>>
>>> Maybe this limitation can be avoided by having an mv-returning function A
>>> set a global variable "mvFunctionReturner" equal to the function A and a
>>> mv-receiver can check that mvFunctionReturner is set according to the
>>> function it called expecting multiple values.  Does this scheme miss any
>>> cases?
>>>
>>> Anyway I have thought a little bit about this and I thought I would pass
>>> it off to the rest of the Parenscripters as a thought experiment.  Assume
>>> you can do a lot more semantic analysis than Parenscript currently does and
>>> transform the compiled source however you want.  But any compiled functions
>>> must still be able to be treated as normal Javascript functions and all and
>>> only functions that should return multiple values appear to return them.
>>>
>>> Cheers,
>>> Red
>>>
>>> _______________________________________________
>>> parenscript-devel mailing list
>>> parenscript-devel at common-lisp.net
>>> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>>>
>>>
>>
>> _______________________________________________
>> parenscript-devel mailing list
>> parenscript-devel at common-lisp.net
>> http://common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>>
>>
>
> _______________________________________________
> parenscript-devel mailing list
> parenscript-devel at common-lisp.net
> http://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/20091101/888aa2e5/attachment.html>


More information about the parenscript-devel mailing list