Newsgroups: comp.lang.scheme
Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!think.com!snorkelwacker.mit.edu!bloom-beacon!dont-send-mail-to-path-lines
From: mkatz@garlic.stanford.EDU (Morris Katz)
Subject: Multiple return values
Message-ID: <9104051955.AA05240@garlic.Stanford.EDU>
Sender: daemon@athena.mit.edu (Mr Background)
Organization: The Internet
References: <1991Apr1.181633.12561@snitor.uucp>
Date: 5 Apr 91 19:55:58 GMT
Lines: 119


   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
--------------------
