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

multiple return values



Our previous round of discussions on multiple return values reached
no consensus.  I want to try again with the very simple, specific
proposal that follows.  The proposal consists of additions and changes
to the formal semantics that appears in R3RS, an informal description,
and a rationale.


FORMAL SEMANTICS

[Notation: The "\" character should be read as a Greek lambda,
and the "!" character should be read as a down-arrow.]

Add a procedure RECEIVE-VALUES whose semantics is given by

    receive_values: E* --> K --> C

    receive_values
      = twoarg (\ e1 e2 k . applicate e1 <> (\ e* . applicate e2 e* k))

Add a procedure RETURN-VALUES whose semantics is given by

    return_values: E* --> K --> C

    return_values = \ e* k . k e*

Either leave the equation of the auxiliary function "single" as is
(equation 1 below), or change it as indicated in equation 2 or as
indicated in equation 3.

    single: (E --> C) --> K

 1. single = \ f e* . # e* = 1 --> f (e* ! 1), wrong "..."

 2. single = \ f e* . # e* >= 1 --> f (e* ! 1), wrong "..."

 3. single = \ f e* . # e* >= 1 --> f (e* ! 1), f unspecified


INFORMAL DESCRIPTION

(RECEIVE-VALUES thunk proc)                                procedure

The first argument is a procedure of no arguments, the second is any
procedure.  Calls the first argument, obtaining 0 or more return
values, and then calls the second argument on the value(s) returned
by the first argument.  See RETURN-VALUES.

    (receive-values (lambda () (return-values 3 7))
                    (lambda (a b) (* a b)))         -->  21



(RETURN-VALUES x ...)                                      procedure

Takes any number of arguments, and returns them as multiple return
values.  [See the rationale for a discussion of how many return values
a continuation expects, and the error conditions that may result when
the expectations are not met.]  See RECEIVE-VALUES.

    (receive-values (lambda () (return-values 'a 'b 'c))
                    vector)                          -->  #(a b c)

    (letrec ((f (lambda (x) (if (zero? x) (g x) (g x))))
             (g (lambda (x) (return-values x (- x))))
             (h (lambda (x)
                  (receive-values (lambda () (f x)) list))))
      (h 3))
                                        -->  (3 -3)

    (letrec ((f (lambda (x) (+ (g x) 13)))
             (g (lambda (x) (return-values x (- x))))
             (h (lambda (x)
                  (receive-values (lambda () (f x)) list))))
      (h 3))
                                        -->  error [equation 1]
                                        -->  (16)  [equation 2 or 3]

    (receive-values (lambda () (return-values)) vector)  -->  #()

    (receive-values (lambda () (return-values 1))
                    (lambda (x . y) y))
                                        -->  ()

    (receive-values (lambda () (return-values))
                    (lambda (x . y) y))
                                        -->  error

    (list (return-values 'a 'b 'c 'd))  -->  (a)

    (car (list (return-values)))  -->  error       [equation 1 or 2]
                                  -->  unspecified [equation 3]


RATIONALE

This proposal is along the lines of multiple return values as in
Common Lisp, but is somewhat simpler and more rational.  The simplicity
of the proposal, as compared to the exposition in CLtL, is attributable
to a better choice of primitives and to Scheme's tail-recursive semantics.

RECEIVE-VALUES and RETURN-VALUES correspond to T3's RECEIVE-VALUES
and RETURN.  The name "RETURN" was rejected because it would confuse
people who are accustomed to the use of RETURN in Common Lisp and
similar Lisps.  Note that these are procedures, not syntax, and that
neither would be essential Scheme.

The arguments to RECEIVE-VALUES are reversed from the way they are in T3.
Feel free to argue the argument order.

Argue also about which equation we should choose for "single".  It
determines what happens in the case where RETURN-VALUES returns to a
continuation that was not created by RECEIVE-VALUES.  Equation 1 says
that in such a case there must be exactly one return value.  Equation 2
says that in such a case there must be at least one value; the extra
values are ignored.  Equation 3 allows zero return values, in which case
the value received by the continuation is unspecified.  No matter which
equation is chosen, there is no difference between returning a single
value in the usual way and returning a single "multiple value" using
RETURN-VALUES.

I didn't give equations for the extreme positions.  The fascist position
would say that it is always an error for RETURN-VALUES to return to a
continuation that was not created by RECEIVE-VALUES.  The commonlisp
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.  If there is sufficient demand, I will post equations for
these.

If we interpret the use of "wrong" in the semantic equations to mean
"is an error" instead of "signals an error", then all of the equations
allow implementations in which Scheme multiple return values are
compatible with the semantics of Common Lisp's multiple return values.
This should make it easier to support a Scheme subset in Common Lisp or
vice versa.

I see no way to implement the proposed procedures in R3RS Scheme, but
most implementations should find it easy to add them.

William Clinger