[comp.lang.scheme] Multiple return values

doug@snitor.uucp (Doug Moen) (04/02/91)

carlton@husc10.harvard.edu (david carlton) writes:
>What do people think about the suggestion to have procedures such as
>the mutation procedures that return an unspecified value in fact return
>no value at all?  This was mentioned as a possibility in the proposal
>to add multiple return values for Scheme, and T seems to be leaning in
>that direction.  I'm not sure what I think about it or, for that
>matter, about the whole multiple return value concept - while there
>are often situations where I want to return multiple values, it is
>hard for me to think of good notation for that sort of thing.

In the latest issue of LISP Pointers, Pavel Curtis (Pavel@Xerox.Com)
discusses the Scheme multiple return value proposal.

The proposal introduces 3 changes to Scheme:

1. (values x ...)
   The procedure 'values' takes an arbitrary number of arguments,
   including none, and returns all of these arguments as its results.

2. (call-with-values producer consumer)
   Invoke the procedure 'producer' with no arguments, then pass all
   of the values returned as arguments to 'consumer'.

3. Continuations can now take any number of arguments, including none.

call-with-values is rather inconvenient to use directly; some sort
of syntactic sugar is needed.  Curtis describes and rejects a new
form 'bind-values', which is similar to muliple-value-bind in Common
Lisp.  He then describes a better solution:  'We are thus considering
allowing a list of variables to appear in place of a single one in
let and let* expressions:
  (let* ((a (foo))
	 (b (bar a))
	 ((c d) (baz a b))
	 (e (mumble a b c d)))
    (frotz a b c d e))


I have a counter-proposal.  I feel there is a much simpler way to
support multiple return values; one which fits in better with the
rest of the language:  multiple return values are represented by lists.
Thus:
  (values x y ...) is replaced by (list x y ..)
  (call-with-values p c) is replaced by (apply c (p))
Finally, I would extend let and let* so that in place of a variable,
any of the forms allowed in the first argument to lambda can be used.
Thus:
  (let ((a (foo))
	((b c) (procedure-which-returns-a-list-of-two-values))
	((first second . rest) (procedure-which-returns-a-list)))
     ...)
This extension to let introduces the following symmetry into the language:
  (let ((<formals> <actuals>)) <body>)
is now equivalent to
  (apply (lambda <formals> <body>) <actuals>)

I think my proposal has two advantages over the one described by Pavel:

1. It is simpler.
   No fundamentally new mechanisms need to be added to the language;
   the only language change is a simple generalization of let and let*.

2. It is more powerful.
   My proposed extension to let makes it easier to use procedures which
   represent multiple return values by a list; IN ADDITION, the new let
   syntax can be used to simplify code which disassembles list structure.
   Also, Scheme provides a rich set of operations on lists.  Any of these
   operations can be used on the value returned by a procedure that
   adheres to the multiple-return-values-are-lists convention.  Perhaps
   I should claim that my proposal is more synergistic.


Curtis Pavel supplies two arguments against representing multiple return
values as lists:  'In addition to being inefficient, though, this has
conceptual problems.  It could be argued that values in programs should
represent conceptual wholes; in many cases, the collection of values
returned by some procedure lack this coherence.'

I don't find either of these arguments compelling.  The `inefficiency'
caused by using lists is probably minor, and in any case, efficiency
has always taken second place to simplicity and expressive power in
the Scheme design philosophy.  The `conceptual problem' caused by using
lists to represent multiple return values is also a non-issue.
Conceptually, a set of return values is a 'tuple'.  A tuple
is an ordered set of elements of different types, addressed by position.
It is common practice to use lists in Scheme to represent tuples;
the Scheme standard even provides procedures like cadr and caddr
to support this practice.  You are using lists as tuples
whenever you use 'apply' to invoke a procedure that takes a fixed
number of arguments.  When Pavel says 'values ... should represent
conceptual wholes' in connection with lists, he is thinking about
the other common use of lists in Scheme, which is to represent arrays
of values of the same type.  (By 'same type', I mean that all of
the values in the list are being used in the same way.)  Scheme
procedures such as reverse and member support the use of lists as 'arrays'.
'Tuples' are just as legitimate an abstraction as 'arrays', and I
think it is legitimate for Scheme programmers to use lists to
represent either of these abstractions.
-- 
Doug Moen | doug@snitor.uucp | uunet!snitor!doug | doug.tor@sni.de (Europe)

jinx@zurich.ai.mit.edu (Guillermo J. Rozas) (04/03/91)

    I think my proposal has two advantages over the one described by Pavel:

    1. It is simpler.
       No fundamentally new mechanisms need to be added to the language;
       the only language change is a simple generalization of let and let*.

    2. It is more powerful.
       My proposed extension to let makes it easier to use procedures which
       represent multiple return values by a list; IN ADDITION, the new let
       syntax can be used to simplify code which disassembles list structure.
       Also, Scheme provides a rich set of operations on lists.  Any of these
       operations can be used on the value returned by a procedure that
       adheres to the multiple-return-values-are-lists convention.  Perhaps
       I should claim that my proposal is more synergistic.


There is another issue which your proposal does not satisfy, while
Pavel's can be made to.

I, and some other people, think that there is no reason (besides
efficiency) nor is it particularly desirable to add multiple return
values to Scheme unless new expressive power is gained.  Certainly
neither proposal adds any as stated.  The form of additional
expressive power that I have in mind is some form of that available in
Common Lisp, by which additional return values can be dropped if not
expected.

In particular, I believe that it should be possible to re-define

(define (quotient x y)
  (values (old-quotient x y)
          (old-remainder x y)))

and have no-one who used the old version of QUOTIENT notice.

Your proposal can't really accommodate that, while Pavel's can
(although it does not require it).

What you propose is a possible implementation of Pavel's proposal if
no new expressive power is gained, but implementations that want to
extend his proposal along these or other lines can do it with his but
not yours.

gyro@cymbal.reasoning.COM (Scott Layson Burson) (04/03/91)

   Date: Mon, 1 Apr 1991 18:16:33 GMT
   From: Doug Moen <snitor!doug@bloom-beacon.mit.edu>

   In the latest issue of LISP Pointers, Pavel Curtis (Pavel@Xerox.Com)
   discusses the Scheme multiple return value proposal.

   [...]

   call-with-values is rather inconvenient to use directly; some sort
   of syntactic sugar is needed.  Curtis describes and rejects a new
   form 'bind-values', which is similar to muliple-value-bind in Common
   Lisp.  He then describes a better solution:  'We are thus considering
   allowing a list of variables to appear in place of a single one in
   let and let* expressions:
     (let* ((a (foo))
	    (b (bar a))
	    ((c d) (baz a b))
	    (e (mumble a b c d)))
       (frotz a b c d e))

I have been using a slightly different syntax for years, e.g.:

     (let ((a b c (foo)))
       ...)

I am very fond of this syntax.  It has one fewer pair of parens than the
one proposed, and also doesn't discriminate visually between the 1-value
case and and the >1-value case -- some might find this a bug, but in a
program full of multiple values I find it a feature.

I have macros that implement this in Common Lisp and T.  (Actually the
syntax they implement contains an additional extension, which again I
like a lot but I don't know if I would make a serious proposal of.  The
idea is that the order in which evaluation and binding are performed is
indicated by depth of nesting of clauses, so that, e.g.:

  (let ((a b (foo))
        ((c d e (bar a b))
         ((f (zot b c e))))
        (m n o (quux)))
    ...)

is equivalent to

  (let ((a b (foo))
        (m n o (quux)))
    (let ((c d e (bar a b)))
      (let ((f (zot b c e)))
        ...)))

The idea here is that all clauses at a given depth are performed in
parallel, and all clauses at depth n are done before/outside of all
clauses at depth >n.  I like this as it allows me essentially to mix the
effects of LET and LET*, and also lets me supply something of a visual
indication of the data flow (the way the example is written, for
instance, is intended to convey that C, D, E, and F do not depend on M,
N, and O, though this is only a convention, NOT a restriction enforced
by the implementation).)

Anyhow, maybe this latter preference of mine is a bit idiosyncratic, but
I think the first proposal is something most everyone would like.

   I have a counter-proposal.  I feel there is a much simpler way to
   support multiple return values; one which fits in better with the
   rest of the language:  multiple return values are represented by lists.

   [...]

I like to write in a style that makes liberal use of multiple values.  I
would be discouraged from doing so on efficiency considerations were
they to be implemented as lists.

I also agree with Pavel that there is a stylistic difference in the way
the two are used.  Consider the situation in C, where you can return
several values from a function by packaging them up into a struct.  I
feel that if I have to declare a struct type solely for the benefit of
the return value of a single function, that is more like "multiple
values" than it is like a structure.  Conversely, if that struct type is
in use in many places already, then it would often make sense (in the
Scheme case) to return it as a list, because much of the time I'm going
to be manipulating the list as a unit rather than destructuring it
(since there are already other functions that take lists like that as
arguments).

-- Scott

carlton@husc10.harvard.edu (david carlton) (04/03/91)

I guess that most of the questions that I have dealing with multiple
return values are on how strictly the number of arguments returned
should be enforced.  That is, should the it be an error if the caller
does not expect the number of values returned by the callee?  There
are, of course, two possibilities here:

1) Yes, they have to match.

2) No, they don't have to match.

What are the pros and cons of each of these possibilities?  The ones
that I can think of are:

1) This is conceptually neater.  After all, if the number of arguments
in a function call has to match, then it is only sensible for the
number of values in a function return to match.  This also fits in
quite well with the continuation passing way of looking at things,
since function returns are function calls to the continuation.  And it
provides more error checking for your programs.

2) If they don't have to match then you can mimic the Common Lisp
behaivour that Jinx wanted, namely to be able to drop unwanted
additional return values.  It is also a bit more efficient, requiring
one fewer error check, but by the same token may open up another
source of difficult-to-find errors.  It allows procedures which
currently return an unspecified value to return no value without
breaking existing code, whereas requiring the number of arguments to
match would screw up code which called such a function in a position
requiring a value.

That last statement itself needs elaboration - where would values be
required in a Scheme program?  (Assuming that we are following
possibility #1, that checking this number is enforced.)  In some
cases, it is clear - for example, the first form in an 'if' statement
should return one value.  Presumably, all but the last form in a
'begin' statement should return no value.  Though that itself is
problematic - what if you call a function which performs a side effect
_and_ returns a useful value?  (append!, say.)  Such a function
clearly has to return a value, but you might also want to call it from
the middle of a 'begin' statement.  So perhaps it would be best to
allow the forms in the middle of a 'begin' statement to return an
arbitrary number of values.  Similarly, the 'eval' part of a repl loop
should be allowed to return an arbitrary number of values.  (Never
mind that the standard doesn't really admit the existence of a repl
loop.)

So what have I left out?  Apologies if this posting seems a bit
simplistic - I'm trying to figure out my opinion on these matters and
the potential implications of my opinion as I write this, so it's not
as polished as it might otherwise be.  Which Schemes already implement
multiple return values?  T is the only one that I know of, but there
must be more.  What are the experiences of people using such systems?

david carlton
carlton@husc9.harvard.edu

tmb@ai.mit.edu (Thomas M. Breuel) (04/03/91)

In article <9104022333.AA00572@cymbal.reasoning.com.>, gyro@cymbal.reasoning.COM (Scott Layson Burson) writes:
|> 
|>    Date: Mon, 1 Apr 1991 18:16:33 GMT
|>    From: Doug Moen <snitor!doug@bloom-beacon.mit.edu>
|> 
|>    In the latest issue of LISP Pointers, Pavel Curtis (Pavel@Xerox.Com)
|>    discusses the Scheme multiple return value proposal.
|>    [...]
|> 
|>    I have a counter-proposal.  I feel there is a much simpler way to
|>    support multiple return values; one which fits in better with the
|>    rest of the language:  multiple return values are represented by lists.
|>    [...]
|> 
|> I like to write in a style that makes liberal use of multiple values.  I
|> would be discouraged from doing so on efficiency considerations were
|> they to be implemented as lists.

I think it is wrong to add multiple return values to Scheme for
efficiency reasons: the gain in efficiency is small to non-existent
for good compilers, and the semantics are messy. 

In order to gain efficiency through the use of a special multiple
return values data type, you have to seriously restrict how you can
manipulate multiple return values (if you treat a set of multiple
return values as a first class object, you might as well use lists,
since the Scheme optimizer would face exactly the same problems in
compiling this new data type efficiently). This adds lots of semantic
hair to the language.

And, the gains in execution speed are dubious at best.  In fact, I
suspect that since modern Scheme implementations have done away with
the stack altogether and allocate all procedure activation frames on
the heap, data structures representing multiple return values would
simply end up on the heap as well. Also, in those cases where multiple
return values could be allocated in a stack-like manner, a Scheme
compiler stands a pretty good chance to eliminate heap allocation even
when they are implemented using lists.

A nice addition to Scheme that would make the use of lists
as multiple return values a little more convenient would be some
form of destructuring. I doubt that the Scheme committee as a whole
could agree on destructuring binding for argument lists (which I would
prefer), but a special form DESTRUCTURING-BIND or DLET would certainly
be nice.

					Thomas.

jonl%kuwait@lucid.COM (Jon L White) (04/04/91)

In Scheme Digest V3 #175, Jinxs says of the Common Lisp MV style:

    Date: 2 Apr 91 18:57:22 GMT
    From: "Guillermo J. Rozas" <jinx@zurich.ai.mit.edu>
    . . . 
    In particular, I believe that it should be possible to re-define

    (define (quotient x y)
      (values (old-quotient x y)
	      (old-remainder x y)))

    and have no-one who used the old version of QUOTIENT notice.


This is just an application of the principle of "referential transparency",
which is THE reason why Common Lisp opted for a multiple-value syntax
rather than fostering an embedding of multiple values into user-created
structures like LISTs or VECTORs.

There are times when this "transparency" causes hiccups -- like the
dissimilarity between passing in "multiple arguments" to a function
and receiving back "multiple values".  After long experience with
both styles of argument passing -- Interlisp's which simply dropped
any extra arguments passed into a function and padded out with NIL's 
any formal parameters that lacked passed-in arguments, and MacLisp's 
which signaled "wrong number of arguments" errors -- the CL community 
decided that "transparency" on the callee's side was counterproductive
(i.e., the client ought to know exactly the argument spectrum of the 
called function) but that "transparency" on the caller's side is 
extremely useful for functions like your QUOTIENT example.  

In short, being ignorant of extra returned information isn't nearly the 
disaster that being ignorant of some required input parameters is.


-- JonL --

jinx@zurich.ai.mit.edu (Guillermo J. Rozas) (04/04/91)

There are a lot of issues in multiple values that haven't been
mentioned.  I like to classify them by code.  Here are some of the
more controversial points.


A: Are the following two expressions equivalent?
(values 1)
1


B: Is the following expression legal?
(begin
  (values 1 2 3)
  4)


C: Is the following expression legal?
(begin
  (values)
  4)


D: Is the following expression legal, and, if so, what does it return?
(let ((x (values 1 2)))
  x)


E: Is the following expression legal, and, if so, what does it return?
(let ((x (values)))
  x)


F: Is the following expression legal?
(call-with-values
  (lambda ()
    (values 1 2 3))
  (lambda (x y)
    (list x y)))


G: Is the following expression legal, and, if so, what does it return?
(call-with-values
  (lambda ()
    (values 1))
  (lambda (x y)
    (list x y)))


H: Does the following work?
(call-with-values
  (lambda ()
    (call-with-current-continuation
      (lambda (k)
        (k 1 2))))
  (lambda (x y)
    (list x y)))


PS: 
 - By equivalent I mean that either can be substituted for the other
   without changing the meaning of the program (ignoring capture of
   free variables).
 - By legal I mean it is not in error.
 - By work I mean it is legal and the "obvious" value(s) is(are)
   returned.

My preferences are:
A: Yes.
B: Yes.
C: Yes.
D: Yes, 1.
E: No.
F: No.
G: No.
H: Yes.
But would accept a compromise where any of the above are left unspecified.
My arm would need a lot of twisting to accept a proposal that requires
any of those decisions to be otherwise.

Although this set of choices may appear inconsistent, the crucial
observation, that points out the asymmetry between APPLY and VALUES is
that there are no implicit procedures, but there are implicit
continuations.  Furthermore, the continuation that VALUES is invoking
is not apparent in the VALUES expression, but the (expression
evaluating to) the procedure in APPLY expressions is.
Given these asymmetries, the decisions need not be the same.

jinx@zurich.ai.mit.edu (Guillermo J. Rozas) (04/04/91)

In article <14592@life.ai.mit.edu> tmb@ai.mit.edu (Thomas M. Breuel) writes:

   I think it is wrong to add multiple return values to Scheme for
   efficiency reasons: the gain in efficiency is small to non-existent
   for good compilers, and the semantics are messy. 

I think you are mistaken about the efficiency issue.  It obviously
depends on whether you use multiple values frequently or not, and if
you do, it is clear that the CONSING behavior of either CPS code or
multiple-values-as-lists is much worse than that of a properly
implemented multiple values facility.

I don't know what you mean by semantics.  If you mean the naive
semantics, I think you are mistaken, because there are several simple
consistent semantics for multiple values that are easy to describe.
If you mean the denotational semantics of Scheme that appear at the
end of R3RS, you are also mistaken, since Will Clinger cleverly
arranged it so that only two items need to be modified to integrate
multiple values into the current language, namely the definition of
``single'', and the expansion of ``begin''.

   In order to gain efficiency through the use of a special multiple
   return values data type, you have to seriously restrict how you can
   manipulate multiple return values (if you treat a set of multiple
   return values as a first class object, you might as well use lists,
   since the Scheme optimizer would face exactly the same problems in
   compiling this new data type efficiently). This adds lots of semantic
   hair to the language.

Your assumption that there is a new multiple-values data type is not
correct.  A simple implementation leaves a special marker on the stack
when multiple values are expected, and VALUES checks for the presence
of the marker and puts the values in the correct registers or stack
locations.  Thus the values are never consed on the heap with no need
for compiler smarts.  There is no need for first-class VALUES objects,
nor are they particularly desirable.

   And, the gains in execution speed are dubious at best.  In fact, I
   suspect that since modern Scheme implementations have done away with
   the stack altogether and allocate all procedure activation frames on
   the heap, data structures representing multiple return values would
   simply end up on the heap as well. Also, in those cases where multiple
   return values could be allocated in a stack-like manner, a Scheme
   compiler stands a pretty good chance to eliminate heap allocation even
   when they are implemented using lists.

You are also mistaken about what modern Scheme implementations do.
All of the modern compilers that I am familiar with try very hard to
use a stack, and resort to heap-allocated closures only when they
can't figure out what else to do.

   A nice addition to Scheme that would make the use of lists
   as multiple return values a little more convenient would be some
   form of destructuring. I doubt that the Scheme committee as a whole
   could agree on destructuring binding for argument lists (which I would
   prefer), but a special form DESTRUCTURING-BIND or DLET would certainly
   be nice.

Since we are supposedly close to agreeing on a macro facility, users
can write their favorite destructurers and make them publicly
available.  I don't think that this is the sort of thing we should
standardize on.

oz@yunexus.yorku.ca (Ozan Yigit) (04/04/91)

In article <JINX.91Apr3160152@chamarti.ai.mit.edu>
jinx@zurich.ai.mit.edu writes:

>My arm would need a lot of twisting to accept a proposal that requires
>any of those decisions to be otherwise.

This is significant, no doubt, but what exactly is it that makes you or
others involved in this discussion think that there is sufficient interest
within the scheme community [at large] to add new syntax/semantics for
multiple return values to the language in the first place?

Is it a foregone conclusion that one of these proposals [or one that is yet
to come] will necessarily be adopted?

Just curious.

oz
---
What ought to disturb us are not mistakes  | Internet: oz@nexus.yorku.ca
in general, but only those of them that we | Uucp: utzoo/utai!yunexus!oz
are powerless to correct.  -- David Miller | Phone: 1+416-736-5257-33976

defrance@uni2a.unige.ch (04/04/91)

<snitor!doug@boom-beacon.mit.edu>  (Doug Moen) debates in a previous posting
about the Scheme multiple return value proposal and his counter-proposal.



Here is my counter-counter-proposal:

  1. (values x ...)
     The procedure 'values' takes an arbitrary number of arguments, including
     none, and returns all of these arguments as its results.


  2. In a nested call, multiple values are bind to subsequent formal args:

     Suppose bar is a function with returns the three values 1 2 4 and
     foo a function with returns a b

     e.g. (define bar (lambda () (values 1 2 4))
          (define foo (lambda () (values a b))

     Then (+ (bar)) would return 7!

     That's quite powerful:

     Do you want the list of values? That's (list (bar))
     But also (list (bar) (foo)) -> (1 2 4 a b)
     Only the first? (define firstval (lambda l (car l)))
                     (firstval (foo)) -> a
                     (firstval (bar) (foo)) -> 1

     And also VERY elegant:

     (call-with-values producer consumer) is replaced by (consumer (producer))

     (let (<formals> <actuals>) <body>) can still be replaced by
     ((lambda <formals> <body>) <actuals>) :

       (let ((a b c) (bar))
          (* (+ a b) c))    ->  12
     which is actually equivalent to
       ((lambda (a b c) (* (+ a b) c))  (bar))  -> 12


It would have at least two (minor?) disadvantages. The first is that since
you can't tell how many values an application will return, Scheme has to wait
until all the actual parameters are evalued before binding the formals, or 
it has to force the order of evaluation. The second is that current mono-valued
standard procedures cannot be redefined as multiple values procedures without
affecting compatibility with previously written code (That could be an
advantage, actually).
          

Maybe the Scheme community can tell me if something is wrong with this
proposal.
Anyway, since this seems to be the only way for everybody down here to
suggest things to the Scheme deciders, I'd like to have as much echo 
as possible on the net, if you like this idea of multiple values.

Bye

-Massimo

---------------------------------------------------------------------------------
MASSIMO DE FRANCESCO                    email: massimo@cuisun.uinige.ch
Computer Science Center          
University of Geneva, SWITZERLAND
---------------------------------------------------------------------------------

pavel@parc.xerox.COM (Pavel Curtis) (04/05/91)

"Guillermo J. Rozas" writes:
> There are a lot of issues in multiple values that haven't been
> mentioned.  I like to classify them by code.  Here are some of the
> more controversial points.

I give below the answers for SchemeXerox.

> A: Are the following two expressions equivalent?
> (values 1)
> 1

Yes.

> B: Is the following expression legal?
> (begin
>   (values 1 2 3)
>   4)

Yes.

> C: Is the following expression legal?
> (begin
>   (values)
>   4)

Yes.

> D: Is the following expression legal, and, if so, what does it return?
> (let ((x (values 1 2)))
>   x)

No; it signals an error about a wrong number of arguments (to the implicit
continuation).

> E: Is the following expression legal, and, if so, what does it return?
> (let ((x (values)))
>   x)

No; same error as above.

> F: Is the following expression legal?
> (call-with-values
>   (lambda ()
>     (values 1 2 3))
>   (lambda (x y)
>     (list x y)))

Ditto.

> G: Is the following expression legal, and, if so, what does it return?
> (call-with-values
>   (lambda ()
>     (values 1))
>   (lambda (x y)
>     (list x y)))

Ditto.

> H: Does the following work?
> (call-with-values
>   (lambda ()
>     (call-with-current-continuation
>       (lambda (k)
>         (k 1 2))))
>   (lambda (x y)
>     (list x y)))

Yes.

> My preferences are:
> A: Yes.
> B: Yes.
> C: Yes.
> D: Yes, 1.
> E: No.
> F: No.
> G: No.
> H: Yes.

Well, Jinx, what a wonder.  We appear to disagree only on D, where you want to
silently ignore extra returned values and SchemeXerox signals an error.  Were
we to agree to leave D unspecified, though, I'd want to do the same with E, F,
and G: passing the wrong number of arguments to a continuation is passing the
wrong number of arguments to a continuation.

On the other hand, we could also say that the implicit single-argument
continuations can have either of the following (implicit) argument list
structures:

	(x)			; The SchemeXerox approach
	(x . ignored)		; The Jinx approach

I could probably live with this compromise as well.

		Pavel Curtis

jonl%kuwait@lucid.COM (Jon L White) (04/05/91)

In Scheme Digest V3 #176

    Date: 3 Apr 91 10:47:43 GMT
    From: "Thomas M. Breuel" <tmb@ai.mit.edu>

    A nice addition to Scheme that would make the use of lists
    as multiple return values a little more convenient would be some
    form of destructuring. I doubt that the Scheme committee as a whole
    could agree on destructuring binding for argument lists (which I would
    prefer), but a special form DESTRUCTURING-BIND or DLET would certainly
    be nice.


In fact, Lisp/370 (predecessor to IBM's "Uncommon Lisp" called
LISP/VM) had automatic destructuring in every lambda argument
position, including the input arguments.  By 1980, such an 
extension was working in parts of MacLisp and in VAX/NIL.  In
fact, it even "destructured" over vectors as well as lists; e.g.:

     (defun foo ((a #(b c) . l) &optional haha) ...)

could be called like  

     (foo '("Note This" #(dollars cents))) 

and the arguments would be bound thus:

       a = "Note This"
       b = dollars
       c = cents
       l = ()

This was particularly useful in the VAX/NIL compiler and assembler
where much more use was made of simple vectors rather than lists.  
We even had a version that would destructure over DEFSTRUCT instances,
in the expectation that "objects" like vectors and structures would 
displace lists as the more common data structure "of choice".

There was no technical difficulty in extending these ideas for Common 
Lisp; but instead a debate arose over whether the pattern should be 
defined to be a data object (i.e., a cons cell implying taking the CAR 
and CDR of the input argument) or to be a program [i.e., for the above
example, write `(,a `#(,b ,c) . ,l) instead of (a #(b c) . l)].  And in 
addition to this debate, there was an undercurrent of thinking that LET
was sacred and primitive in it's historic, one-to-one definition.  So 
that killed that.  Too bad.  I'm always having to intersperse lines of 
LET's amongst multiple incarnations of DESTRUCTURING-BIND, as well as 
with MULTIPLE-VALUE-BIND's.




re: Date: 3 Apr 91 21:01:50 GMT
    From: "Guillermo J. Rozas" <jinx@zurich.ai.mit.edu>

    There are a lot of issues in multiple values that haven't been
    mentioned.  I like to classify them by code.  Here are some of the
    more controversial points. . . . 

Your particular suggestions look fine to me, but what's missing from
the dialogue is the concept, elaborately worked out in the CL world,
of constructs that (1) truncate multiple-values down to one value, 
and (2) "pass back" multiple values.  For example, your case D easily 
falls out of one of the obvious rules here for (1) [unless there is to 
be a radical change to the syntax/semantics of LET, to accommodate 
either destructuring or multiple values.]



-- JonL --

gumby@Cygnus.COM (David V. Wallace) (04/05/91)

   Date: 4 Apr 91 21:01:00 GMT
   From: jonl%kuwait@lucid.COM (Jon L White)

   Your particular suggestions look fine to me, but what's missing from
   the dialogue is the concept, elaborately worked out in the CL world,
   of constructs that (1) truncate multiple-values down to one value, 
   and (2) "pass back" multiple values.

I always enjoyed the comment in the Chine Ual that read (for several
editions) "Multiple values are not returned through an unwind-protect.
I'd like to fix it but it's hard."

When it finally was fixed that comment was sadly not replaced by any
note indicating how difficult it in fact turned out to have been.

carlton@husc10.harvard.edu (david carlton) (04/05/91)

In article <JINX.91Apr3160152@chamarti.ai.mit.edu> jinx@zurich.ai.mit.edu (Guillermo J. Rozas) writes:
   There are a lot of issues in multiple values that haven't been
   mentioned.  I like to classify them by code.  Here are some of the
   more controversial points.

...

   E: Is the following expression legal, and, if so, what does it return?
   (let ((x (values)))
     x)

...

   My preferences are:
   E: No.

   But would accept a compromise where any of the above are left unspecified.
   My arm would need a lot of twisting to accept a proposal that requires
   any of those decisions to be otherwise.

   Although this set of choices may appear inconsistent, the crucial
   observation, that points out the asymmetry between APPLY and VALUES is
   that there are no implicit procedures, but there are implicit
   continuations.  Furthermore, the continuation that VALUES is invoking
   is not apparent in the VALUES expression, but the (expression
   evaluating to) the procedure in APPLY expressions is.
   Given these asymmetries, the decisions need not be the same.

I think that I agree with you everywhere but here - I would probably
want to allow E as well, though it should be undefined what happens if
the value returned by the expression is ever used.  This would nicely
solve the problem of what to do with the special forms and functions
that currently return undefined values - they could simply not return
a value.  Of course, you could do that in any case, but then you would
break currently legal code that calls those functions from locations
whose continuations expect a single argument.

Not sure, though - I'm not even sure that I would want to allow

(let ((x (values 1 2)))
  x)

david carlton
carlton@husc9.harvard.edu

kers@hplb.hpl.hp.com (Chris Dollin) (04/05/91)

It's interesting to watch this discussion develop. I have long detested the
cack-handed way Common Lisp handles multiple values; this is, of course,
because I have used a language which has handled multiple values since its
inception, rather than having them grafted on at a later stage.

That language is Pop11 (descended from Pop2). Now, it is clear that the way Pop
handles multiple values (see shortly) is, as it stands, entirely unsuitable for
Scheme; but insights from their use may be applicable. I'll try to leave my
comments as unbiased as possible.

Unlike CL, Pop multiple values can appear in any context; if a function returns
multiple results, then the caller must be prepared to deal with them. This
means that one cannot just extend an existing function with an extra result and
expect old code to continue working. In practice, this does not seem to have
many bad consequences; the addition of a single new name to the namespace (the
new version of the function), with the ``old'' function being redefined as one
which calls the new function and discards the result, is sufficient. Of course,
if this happens several times, you may have to restructure your code, if you
don't like the plethora of new names.

However, there's more. As a consequence of the way multiple values are defined,
(and of the way they're implemented), *multiple values accumulate*. Suppose
that F takes one argument and delivers two results, G ditto, and H requires
four arguments. Then (H (F x) (G y)) is sensible and applies H to the quartet
of values returned by F and G.

Someone discussed what would happen with BEGIN. An alternative not mentioned is
that (BEGIN E1 ... En) would return *all* the results of the Ei (hmm ... just
like a sequential version of VALUES). One might want to add functions to
control this possible explosion of values: NONE (takes arbitrary number of
args, no results), INITIAL (takes non-zero number of args, returns first),
FINAL (non-zero number of args, returns last).

This approach has the possible disadvantage that it is no longer possible to
define functions that return ``possibly useful'' results that will be quietly
discarded if used in a no-value context; again, experience in Pop suggests that
this is not a problem.

[Why did I say Pop's multiple values are unsuitable for Scheme? Because they
are implemented by simply using a stack, and making it visible to the
programmer. There is *no* special checking for number-of-arguments-passed or
number-of-results-returned; if F is defined as, say, add-1, then (F 1 2)
returns multiple values, viz, (1 3). Conversely, if G expects two arguments,
and delivers (eg) their sum, then (F 2 (G 3)) delivers 6. The surprising thing
is that this free-wheeling approach to arity results in few errors, most of
which are detected very quickly (stack underflow is, of course, checked for).
However, I do not see this as a sensible change of approach for Scheme; it
would make more sense in this context to keep arity-checking for calls.]


--

Regards, Kers.      | "You're better off  not dreaming of  the things to come;
Caravan:            | Dreams  are always ending  far too soon."

mkatz@garlic.stanford.EDU (Morris Katz) (04/06/91)

   Date: Mon, 1 Apr 1991 18:16:33 GMT
   From: Doug Moen <snitor!doug@bloom-beacon.mit.edu>
   Organization: Siemens Nixdorf

   In the latest issue of LISP Pointers, Pavel Curtis (Pavel@Xerox.Com)
   discusses the Scheme multiple return value proposal.

   The proposal introduces 3 changes to Scheme:

   1. (values x ...)
      The procedure 'values' takes an arbitrary number of arguments,
      including none, and returns all of these arguments as its results.

   2. (call-with-values producer consumer)
      Invoke the procedure 'producer' with no arguments, then pass all
      of the values returned as arguments to 'consumer'.

   3. Continuations can now take any number of arguments, including none.

   call-with-values is rather inconvenient to use directly; some sort
   of syntactic sugar is needed.  Curtis describes and rejects a new
   form 'bind-values', which is similar to muliple-value-bind in Common
   Lisp.  He then describes a better solution:  'We are thus considering
   allowing a list of variables to appear in place of a single one in
   let and let* expressions:
     (let* ((a (foo))
	    (b (bar a))
	    ((c d) (baz a b))
	    (e (mumble a b c d)))
       (frotz a b c d e))


   I have a counter-proposal.  I feel there is a much simpler way to
   support multiple return values; one which fits in better with the
   rest of the language:  multiple return values are represented by lists.
   Thus:
     (values x y ...) is replaced by (list x y ..)
     (call-with-values p c) is replaced by (apply c (p))
   Finally, I would extend let and let* so that in place of a variable,
   any of the forms allowed in the first argument to lambda can be used.
   Thus:
     (let ((a (foo))
	   ((b c) (procedure-which-returns-a-list-of-two-values))
	   ((first second . rest) (procedure-which-returns-a-list)))
	...)
   This extension to let introduces the following symmetry into the language:
     (let ((<formals> <actuals>)) <body>)
   is now equivalent to
     (apply (lambda <formals> <body>) <actuals>)

Ilike this extension to let, but I believe that it is completely independent of
the question as to whether muliple values are represented as lists.

   I think my proposal has two advantages over the one described by Pavel:

   1. It is simpler.
      No fundamentally new mechanisms need to be added to the language;
      the only language change is a simple generalization of let and let*.

   2. It is more powerful.
      My proposed extension to let makes it easier to use procedures which
      represent multiple return values by a list; IN ADDITION, the new let
      syntax can be used to simplify code which disassembles list structure.
      Also, Scheme provides a rich set of operations on lists.  Any of these
      operations can be used on the value returned by a procedure that
      adheres to the multiple-return-values-are-lists convention.  Perhaps
      I should claim that my proposal is more synergistic.


   Curtis Pavel supplies two arguments against representing multiple return
   values as lists:  'In addition to being inefficient, though, this has
   conceptual problems.  It could be argued that values in programs should
   represent conceptual wholes; in many cases, the collection of values
   returned by some procedure lack this coherence.'

   I don't find either of these arguments compelling.  The `inefficiency'
   caused by using lists is probably minor, and in any case, efficiency
   has always taken second place to simplicity and expressive power in
   the Scheme design philosophy.  

Efficiency has only taken second place when there is a strong semantic reason
for doing things in a way that is not the most efficient, and when there has
been a belief that there is a fairly efficient means of implementing the less
than most efficient semantics.  In particular, the Scheme community has often
selected a semantics which has significant special cases that can be
implemented very efficiently so that the user only pays a performance cost when
features offered by the less than most efficient mechanism are utilized.
Call-with-current-continuation is a classic example of such a trade off.  It is
more powerful than catch and throw in Common-lisp, us less efficient to
implement in the general case, but can often be implemented very efficiently
when used for cases in which catch and throw would have sufficed.  I believe
that your proposal fails to meet this level of scrutiny.

Finally, I have not seen addressed here the issue which actually sunk my
multiple values proposal.  (I have not read Pavel's article, so I do not know
if he addresses the issue or not.)  The disagreement revolved around whether
there should be an arity? function which returns info about the arity of a
procedure or reified continuation.  There were several suggestions about how to
handle arity:
1)  (arity? proc number) - Returns #t if PROC can be called with NUMBER values.
2)  (arity? proc) - Returns the number of values required by PROC.
    (rest? proc) - Returns #t if PROC has a rest argument
3)  My suggestion was for (arity? proc) which returns 3 multiple values: the
    minimum number of values required by PROC, the maximum number of values
    accepted by PROC, not including the rest arg (this value would always equal
    the first value for implementation without optional args), and whether PROC
    expects a rest arg (either #t or #f).
The Scheme community basically divided into 3 camps on the arity issue:
1)  The arity question should not be askable (a small minority).
2)  Acceptablity of a given arity should be askable, but a querry about the
    range of acceptable arities should not (case 1 above).
3)  Full arity information should be retrievable (cases 2 and 3 above).  
For a complete recap of these arguements, see the archives of about 2 years
ago.
--------------------
Morry Katz
katz@cs.stanford.edu
--------------------

oz@gpu.utcs.utoronto.ca (04/06/91)

[from oz@nexus.yorku.ca]

An article of mine [responding to jinx, about his arm needing twisting] that
I thought was cancelled seem to have made it out for some reason, please
ignore it. I later re-worded the article to be more thoughtful and less
confrontational, and have received meaningful responses to it.

sorry for any misunderstanding.		oz
---
Ps: Here is a mistake I was powerless to correct ;-)
---
What ought to disturb us are not mistakes  | Internet: oz@nexus.yorku.ca
in general, but only those of them that we | Uucp: utzoo/utai!yunexus!oz
are powerless to correct.  -- David Miller | Phone: 1+416-736-5257-33976