[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

multiple return values (LONG)



*** Note: this is a relatively long message. ***

    (1) I agree with ALAN and Jinx that it seems pretty useless to specify
    a multiple value capability and not go beyond Will's alternative #1.
    Since I intend to implement Common Lisp on top of Scheme, I will want
    to extend the essential capability anyway.  (Are ALAN and Jinx arguing
    for #2 or #3?)

I like #2.  More about this below.

    (2) In his rationale, Will said "The Common Lisp position would say
    that when zero values are returned to a continuation that is expecting
    one value, then the symbol NIL is passed to the continuation."  I
    think we could specify this to be "the false value" or "the empty
    list" and remain compatible with Common Lisp without having to make
    the symbol NIL noteworthy in Scheme.

I don't like arbitrary values created.  Again, more on this below.

    (3) Will's proposal does not mention CALL-WITH-CURRENT-CONTINUATION.
    It seems that a continuation object should accept an arbitrary number
    of arguments if we take alternative #2 or #3.  If so, we could define
    (RETURN-VALUES A B) to be the same as (CALL/CC (LAMBDA (K) (K A B))).

No and yes, respectively.  Again, see below.

    (4) It seems to me that the hairiest part of the multiple value
    "feature" in Common Lisp is MULTIPLE-VALUE-PROG1.  Something like this
    is needed, at least "behind the scenes", if we expect DYNAMIC-WIND
    (etc.) to pass through multiple values.  We could express
    (MULTIPLE-VALUE-PROG1 A B ...) as

    (receive-values A (lambda L B ... (apply return-values L))) ,

    but that is pretty expensive.  Is this common enough to require
    standardization so implementors could work on more efficient
    mechanisms?

I think it is worth thinking about, but given that the above is
sufficient, I don't think there is a need to standardize.

    (5) JAR finds the proposed feature "to be pretty unuseable unless
    there is some syntactically sugared way to use RECEIVE-VALUES."  If
    this is true, I think we must agree on what sugar to add.  What's the
    point of standardizing on something that is too cryptic to be used by
    anyone?

    I like T's RECEIVE, which I understand to be defined as

    (receive <formals> <m-v-expression> . <body>)

    ==> (receive-values (lambda () <m-v-expression>)
			(lambda <formals> . body>))

    The key for me is that <formals> is a complete lambda list, possibly
    containing "optional" and "rest" arguments, and is not just a list of
    "required" identifiers.  This seems more flexible than Common Lisp's
    MULTIPLE-VALUE-BIND.

    If we keep the name RECEIVE-VALUES, then the name RECEIVE is ok here,
    but I would prefer something like MULTIPLE-VALUE-LET.

What about MULTIPLE-VALUE-BIND?  The syntax is not like LET at all,
and it really is similar to the CL construct.

    (6) Let's get down to brass tacks and argue about names!  I'm bothered
    by the "return" in RETURN-VALUES.  As Will pointed out, the name
    RETURN would be confusing to refugees from other Lisps.  However,
    RETURN-VALUES still seems to imply an exit from the calling procedure.
    A user might ask whether the procedure

     (LAMBDA (A B C) (+ A (RETURN-VALUES B) C))

    returns the sum of A, B, and C or just B?  I think we all intend for
    RETURN-VALUES to "return" to its caller (the middle of the body), not
    to that procedure's caller.  I suggest renaming RETURN-VALUES to be
    MULTIPLE-VALUES.

    Likewise, the "receive" in RECEIVE-VALUES seems strange; I would
    expect the complementary routine to be SEND-VALUES.  How about
    CALL-WITH-MULTIPLE-VALUES (call/mv?) by analogy with CALL-WITH-
    CURRENT-CONTINUATION (call/cc)?  (I'm not sure if I'm joking!)

I don't care much about the names of procedures, but I agree with GLS.
What's wrong with VALUES?  Another possibility for RECEIVE-VALUES is
MULTIPLE-VALUE-COMPOSE, or even, CALL-EXPECTING-MULTIPLE-VALUES.

------------------------------------------------------------------------------

Well, here is my argument for proposal #2:

I think that there is a very important symmetry between procedure
invocation and continuation invocation.  Indeed, if the code is CPS
converted, they become the same thing.

At apply time there is strict number of arguments checking, although
procedures, through optional or rest arguments, may allow a range of
numbers of arguments.  I think it is important to be consistent here
and make continuation invocation be the same.  Thus when an explicit
continuation is provided (as in the case of RECEIVE-VALUES), the
number of arguments (values) should be checked just as strictly.

Thus, I think that both

(receive-values (lambda () (values 1 2 3))
		(lambda (a b) ...))

(receive-values (lambda () (values 1))
		(lambda (a b) ...))

should be in error.  The CL semantics can easily be implemented on top
of this by making the actual procedure passed to RECEIVE-VALUES accept
any number of arguments, and then initialize the ones not provided
with #f.  Implementations can even be more lax since being in error
does not mean that one is signalled.

Now, wait a minute... This seems like an argument for #1, right?

Well, the difference is that there are (as far as I can tell) no
implicit procedures, but there certainly are implicit continuations,
and the behavior can be different.

The rule is as follows: an implicit continuation may be called with
MORE values than it expects, and these extra values are ignored.  Thus
returning zero values to a continuation that expects one should be an
error (as opposed to creating a #f), but returning 2 should be valid.
This makes all the cases that I'm interested in work, yet allows the
error checking when multiple values are explicitely asked for.

Thus, assuming that QUOTIENT returns 2 values,

(let ((x (quotient (fact 100) (expt 2 23))))
  ...)

should work, but

(receive-values (lambda () (quotient (fact 100) (expt 2 23)))
		(lambda (x) ...))

should be in error.

In case you are wondering, the example with the implicit continuation
is equivalent to

(receive-values (lambda () (quotient (fact 100) (expt 2 23)))
		(lambda (x . ignore) ...))

As far as continuation objects obtained with CWCC are concerned, they
should accept multiple arguments if the corresponding continuation
accepted multiple values, thus

(receive-values (lambda ()
		  (cwcc (lambda (here)
			  (here 2 3))))
		(lambda (x y)
		  (list x y)))

should return (2 3),

(receive-values (lambda ()
		  (cwcc (lambda (here)
			  (here 2 3))))
		(lambda (x)
		  x))

should be in error, and

(let ((x (cwcc (lambda (here)
		 (here 2 3)))))
  x)

should return 2.

Thus the number of arguments that continuations expect is not
arbitrary, but it is the case that

(return a b c)

is equivalent to

(cwcc (lambda (k) (k a b c)))

Having said all this, I'll also say that I'm afraid that we will not
be able to standardize.  JAR feels relatively strongly about proposal
#1, and I think that other people do also.  I feel relatively strongly
agains standardizing on the CL standard, since I don't believe in the
random NILs (or #Fs) being created on demand, so I'm therefore opposed
to #3, and I would not be surprised if other people had their own pet
theories.