[parenscript-devel] A simpler way to do multiple value return? (Was: PS test failures in 7be9b45)
Daniel Gackle
danielgackle at gmail.com
Mon Sep 3 20:36:29 UTC 2012
Um, and duh: implicit arg technique fails to do any passthrough at all in
most cases:
(defun foo ()
(multiple-value-bind (a b) (bar) // ignore a
b))
(defun bar ()
(baz))
(defun baz ()
(values 10 20))
MV semantics ought to hop along the chain FOO->BAR->BAZ and back, but
here BAR does nothing to forward the arguments list, so BAZ never gets an
implicit MV array to populate. In other words, implicit arg in general is
good
for one call only, which is not good enough.
I'm surprised I didn't see this before. It only became obvious to me
after that last point about tail calls.
Daniel
On Mon, Sep 3, 2012 at 12:58 PM, Daniel Gackle <danielgackle at gmail.com>wrote:
> You've convinced me about subtle bugs. But I would like to get clear
> about the root difficulty. I don't think this quite captures it:
>
> < Except as you point out the multiple-values array will now be passed
> on to any functions whose value BAR returns >
>
> ... because if BAR *returns* that other function's value(s) then we
> actually want whatever it puts in the MV array - yes? That is, in the
> following:
>
> (defun foo ()
> (multiple-value-bind (a b) (bar) // ignore a
> b))
>
> (defun bar ()
> (apply #'baz arguments))
>
> (defun baz ()
> (values 10 20))
>
> ... FOO should return 20, so it's good that the MV semantics pass
> through BAR to BAZ. The problem comes when BAR *doesn't* return its
> call to BAZ, and BAZ has populated the MV array with bogus values:
>
> (defun bar ()
> (apply #'baz arguments)
> nil)
>
> Now FOO should clearly not return 20. Does this illustrate what you
> are objecting to?
>
> What I find interesting is how the different implementations we've been
> discussing all seem to break on the same thing. Correct compilation of
> MVR requires distinguishing between function calls in a tail position
> (where MV passthrough is wanted) and those not in a tail position
> (where MV passthrough is a bug). That is, the granularity within which
> MV state should be shared and outside which it must not leak is: a
> chain of function calls in the tail position.
>
> That explains why the proposed implementations so far are incorrect:
> implicit argument as described above, MV_RETURNS (because MV state is
> global), and foo.mv (because MV state is per-function [1]). None of
> them works at this granularity.
>
> So two questions: 1) is this explanation of the difficulty correct?
> 2) Is there a way for PS to ensure that MV state can't leak outside a
> chain of tail calls? A naive way to do it would be to generate code to
> clear MV state after every non-tail call.
>
> Daniel
>
> [1] With respect to the foo.mv implementation, we had talked about
> things breaking on recursion. It's worth noting that recursion and MV
> work fine together as long as the MV calls are tail calls. e.g. BAR
> returns 6 here:
>
> (defun foo (list)
> (cond ((null list) (values 1 2 3))
> (t (foo (cdr list)))))
>
> (defun bar ()
> (multiple-value-bind (a b c) (foo '(blah blah))
> (+ a b c)))
>
>
> On Mon, Sep 3, 2012 at 8:55 AM, Vladimir Sedach <vsedach at gmail.com> wrote:
>
>> > I'd be interested to see examples that break on the implicit MV arg
>> > that don't already break on &key. Passing the arguments pseudo-array
>> > on, for example, shouldn't break. It just throws the responsibility
>> > for parsing the MV arg on to somebody else, which in non-MV-aware
>> > cases will typically ignore it and in MV-aware cases will handle it
>> > according to the protocol.
>>
>> If you call a function FOO with &key parameters you're expecting it to
>> be a PS function, and if function FOO calls other functions they don't
>> care. You can make the same argument for calling a function BAR
>> expecting multiple values. Except as you point out the multiple-values
>> array will now be passed on to any functions whose value BAR returns,
>> which might or might not lead to subtle bugs. So I am really opposed
>> to this approach.
>>
>> Vladimir
>>
>> > It strikes me that the implicit MV arg is a generalization of the &key
>> > mechanism where the key is not a symbol (string) but a sentinel
>> > object. The big break with current practice is that it's hidden rather
>> > than specified by the user at the source level. Makes me wonder if a
>> > different syntax than MULTIPLE-VALUE-BIND might help - something to
>> > make the underlying mechanism less unexpected...
>> >
>> > To be sure, it's a hack and not at all what one would do with proper
>> > access to the internals. But I wonder if it's as good as we're likely
>> > to get by way of a correct implementation.
>> >
>> >
>> > On Sun, Sep 2, 2012 at 6:28 PM, Vladimir Sedach <vsedach at gmail.com>
>> wrote:
>> >>
>> >> > We may have crossed a wire here. When you said, "You can do that with
>> >> > a global table instead of setting a property on the function object,"
>> >> > I thought you had in mind a global table keyed by function *name*,
>> >> > which is why I asked about lambdas since they have no names. JS won't
>> >> > let you use a function object as a key so one would have to concoct
>> >> > some naming scheme.
>> >>
>> >> For my first prototype for the new MV mechanism, that's what I thought
>> >> and used gensyms. But then I tried foo[<function object>] and that
>> >> works in both FF and CL-JS. But looking at ECMAScript, property
>> >> identifiers do indeed have to be JavaScript String objects
>> >> (http://ecma-international.org/ecma-262/5.1/#sec-8.10).
>> >>
>> >> > Moreover, if it worked for MV, the sentinel idea could be used to tag
>> >> > anything else we wanted into the call. Seems like that could be
>> pretty
>> >> > powerful.
>> >> >
>> >> > Where does this break?
>> >>
>> >> It wouldn't work for calling JavaScript functions that used the
>> >> arguments pseudo-array, either for arbitrary arity or for passing
>> >> arguments on. There's lots of JS functions around that do things like
>> >> foo.apply(null, slice(arguments, x)) or whatever.
>> >>
>> >> > p.s. There's also a way to communicate exactly how many return values
>> >> > are desired, short-circuiting any computation that might be needed to
>> >> > generate the full VALUES list. (I think this point is independent of
>> >> > the above idea but I'll adapt the same examples.) Suppose we have:
>> >>
>> >> In general you have to evaluate all the expressions given to values
>> >> for their side-effects, so this would only save a few assignments.
>> >>
>> >> Vladimir
>> >>
>> >> _______________________________________________
>> >> 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/20120903/6335d7c3/attachment.html>
More information about the parenscript-devel
mailing list