Newsgroups: comp.lang.scheme
Path: utzoo!utgpu!news-server.csri.toronto.edu!torsqnt!snitor!doug
From: doug@snitor.uucp (Doug Moen)
Subject: Multiple return values
Message-ID: <1991Apr1.181633.12561@snitor.uucp>
Organization: Siemens Nixdorf
References: <1991Mar26.155905.12906@daffy.cs.wisc.edu> <kiran.670017354@copper> <CARLTON.91Mar30173128@husc10.harvard.edu>
Date: Mon, 1 Apr 1991 18:16:33 GMT

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)
