# Motivations for multiple return values feature?

### George Caswell

2002/04/08 15:06:482002/04/08
To:

Hi,

What are the motivations behind Scheme's multiple return values feature?
Is it meant to reflect the difference in intent, or is there a
runtime-practical reason?

That is, is the multiple return values feature just a box that says "This
is not a list or a vector, please don't treat it as one or assume that the
values inside are directly related to one another"? Or is there another
purpose of which I'm not currently aware?

Thanks in advance for your time in helping me understand this feature. As
part of my interest in OS-shell data models (see the "Scheme datatypes at
system level" thread for details) I'm studying the different data models and
semantic constructs offered by different languages - and the multi-value
return one is always something that just made me wonder "why?" It seemed like
returning a list or vector would serve pretty much the same purpose. On the
other hand, making a value's meaning a part of the type system is potentially
less error-prone than representing a value's meaning within the type system, I
think..

---GEC
New projects page: http://home.attbi.com/~sieg_haro/
(M-x depeche-mode)
"Must... Finish... Zaku..."

### Thant Tessman

2002/04/08 15:49:042002/04/08
To:
George Caswell wrote:

> What are the motivations behind Scheme's multiple return values feature?

It's an optimization hack. It never should have been added to the language.

-thant

### Joe Marshall

2002/04/08 15:41:312002/04/08
To:
When you call a function, you pass in multiple arguments.
Returning a value is simply calling a continuation, so why
shouldn't you be able to pass multiple values?

Yes, you could return a list or vector, but by abstracting
away the implementation of multiple value return from the
semantics, perhaps something more clever could be done.

"George Caswell" <sieg...@attbi.com> wrote in message

### MJ Ray

2002/04/09 5:50:162002/04/09
To:

Why do you say that?

### MJ Ray

2002/04/09 5:52:092002/04/09
To:
George Caswell <sieg...@attbi.com> wrote:
> That is, is the multiple return values feature just a box that says "This
> is not a list or a vector, please don't treat it as one or assume that the
> values inside are directly related to one another"? Or is there another
> purpose of which I'm not currently aware?

You know that you can make them a list if you want with:

(call-with-values
form-returning-values
list)
?

### MJ Ray

2002/04/09 5:50:162002/04/09
To:

Why do you say that?

### MJ Ray

2002/04/09 5:52:092002/04/09
To:

George Caswell <sieg...@attbi.com> wrote:
> That is, is the multiple return values feature just a box that says "This
> is not a list or a vector, please don't treat it as one or assume that the
> values inside are directly related to one another"? Or is there another
> purpose of which I'm not currently aware?

You know that you can make them a list if you want with:

(call-with-values
form-returning-values
list)
?

### George Caswell

2002/04/09 13:28:192002/04/09
To:

Right. My question, however, is what's the point? I know there must have
to the language, a reason why someone would want to "return multiple values"
instead of "return a list of values", I just don't know what it is. One
perspective's already been posted. Do you have another you'd like to share?
(That is, are you prepared to attempt to answer my question in some kind of
useful way?)

### MJ Ray

2002/04/09 13:52:052002/04/09
To:
George Caswell <sieg...@attbi.com> wrote:
> (That is, are you prepared to attempt to answer my question in some kind of
> useful way?)

It's difficult for me to say whether there is *another* purpose that you are
not aware of, because I cannot read your mind. Perhaps you should phrase

Let me have another go. I do not see a problem with saying something like:

(call-with-values (lambda () (function-returning-3values params))
(lambda (v1 v2 v3)
(function-to-process v1 v2)
(another-function v3)))

In fact, when it fits the situation well, it saves some "noise work" with
let, car, cdr, list-ref etc and allows the real method to shine through. My
Opinion Only.

### MJ Ray

2002/04/09 13:52:052002/04/09
To:

George Caswell <sieg...@attbi.com> wrote:
> (That is, are you prepared to attempt to answer my question in some kind of
> useful way?)

It's difficult for me to say whether there is *another* purpose that you are

### Jens Axel Søgaard

2002/04/09 13:54:342002/04/09
To:

"George Caswell" <sieg...@attbi.com> skrev i en meddelelse

> What are the motivations behind Scheme's multiple return values
feature?
> Is it meant to reflect the difference in intent, or is there a
> runtime-practical reason?

I imagine the reason being this.

Let's say that need f is called by g. g needs severeal values from f.
Without multiple value return, f packs the values in a list (or vector),
which is
is passed to g. g then immediately unpacks the list.

With multple values, the values are just pushed on the stack. Thus no
packing and unpacking is done.

Whether this should be called an optimization hack or not, is up to you.

--
Jens Axel Søgaard

We don't need no side-effecting We don't need no allocation
We don't need no flow control We don't need no special-nodes
No global variables for execution No dark bit-flipping for debugging
Hey! did you leave the args alone? Hey! did you leave those bits alone?
(Chorus) -- "Another Glitch in the Call", a la Pink Floyd

### Dorai Sitaram

2002/04/09 14:24:192002/04/09
To:
In article <2002040917524...@bloom-beacon.mit.edu>,

There is already the procedure apply that can do the
unpacking without let, car, cdr, list-ref, etc.

(apply (lambda (v1 v2 v3)

(function-to-process v1 v2)
(another-function v3))

(function-returning-3values params)) ;but as a list this time

This is actually less noisy (and therefore,
arguably more shiny) than the call-with-values
version.

### George Caswell

2002/04/09 14:51:362002/04/09
To:
On Tue, 9 Apr 2002, MJ Ray wrote:

> George Caswell <sieg...@attbi.com> wrote:
> > (That is, are you prepared to attempt to answer my question in some kind of
> > useful way?)
>
> It's difficult for me to say whether there is *another* purpose that you are
> not aware of, because I cannot read your mind. Perhaps you should phrase
>

I did explain my current (limited) understanding of multiple return
values. About the only reason I could fathom for its existence was in order
to make the representation match the intent. I humbly suggest that, in order
to make your responses useful, you pay attention to what you're reading.

> Let me have another go. I do not see a problem with saying something like:
>
> (call-with-values (lambda () (function-returning-3values params))
> (lambda (v1 v2 v3)
> (function-to-process v1 v2)
> (another-function v3)))
>

Or alternately:

(apply (function-returning-3-values-as-list) another-function)

> In fact, when it fits the situation well, it saves some "noise work" with
> let, car, cdr, list-ref etc and allows the real method to shine through. My
> Opinion Only.
>

Fair enough. Thank you for your input.

### Barry Margolin

2002/04/09 14:55:492002/04/09
To:

George Caswell <tets...@users.sourceforge.net> wrote:
>
> Hi,
>
> What are the motivations behind Scheme's multiple return values feature?
>Is it meant to reflect the difference in intent, or is there a
>runtime-practical reason?

As others have mentioned, it allows for some runtime optimization.

As for the intent reflected, I think it's orthogonality with argument
passing. Consider that it isn't really necessary to pass multiple
arguments in separate variables -- all functions could take a single
argument, and if you needed to pass multiple arguments you could pass a
list, which the function would take apart to get the individual arguments.

But in fact, we don't require programmers to package up multiple input
values that way. So why should we require them to do so for multiple
output values?

--
Barry Margolin, bar...@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

### Dorai Sitaram

2002/04/09 15:50:192002/04/09
To:
In article <VOGs8.16$iw5...@paloalto-snr2.gtei.net>, Barry Margolin <bar...@genuity.net> wrote: >In article <Pine.LNX.3.96.102040...@adamant.attbi.com>, >George Caswell <tets...@users.sourceforge.net> wrote: >> >> Hi, >> >> What are the motivations behind Scheme's multiple return values feature? >>Is it meant to reflect the difference in intent, or is there a >>runtime-practical reason? > >As others have mentioned, it allows for some runtime optimization. > >As for the intent reflected, I think it's orthogonality with argument >passing. Consider that it isn't really necessary to pass multiple >arguments in separate variables -- all functions could take a single >argument, and if you needed to pass multiple arguments you could pass a >list, which the function would take apart to get the individual arguments. I think the intent reflects a concern with symmetry rather than orthogonality. Orthogonality implies that multiple-values brings something new to the expressiveness table, and it doesn't. >But in fact, we don't require programmers to package up multiple input >values that way. So why should we require them to do so for multiple >output values? Packing with VALUES isn't any easier than packing with LIST, and unpacking with CALL-WITH-VALUES isn't any easier than unpacking with APPLY. There is no lightening of burden. Indeed both unpacking approaches rely on Scheme allowing lambdas with multiple _arguments_, and since multiple arguments don't rely on multiple (output) values, the symmetry rationale is also shaky. ### Ben Goetter 未読、 2002/04/09 16:01:382002/04/09 To: Quoth Barry Margolin: > But in fact, we don't require programmers to package up multiple input > values that way. So why should we require them to do so for multiple > output values? 1. Distaste for the violence that VALUES performs on R4RS APPLY and any Scheme evaluation model based on that APPLY. 2. An abiding affection for first-class values (little-v). In the abstract, I like the idea of a n-ary continuation. Just wish it wasn't so fugly. ### Barry Margolin 未読、 2002/04/09 16:29:412002/04/09 To: In article <a8vglr$79n$1...@news.gte.com>, Dorai Sitaram <ds...@gte.com> wrote: >In article <VOGs8.16$iw5...@paloalto-snr2.gtei.net>,
>Barry Margolin <bar...@genuity.net> wrote:
>>George Caswell <tets...@users.sourceforge.net> wrote:
>>>
>>> Hi,
>>>
>>> What are the motivations behind Scheme's multiple return values feature?
>>>Is it meant to reflect the difference in intent, or is there a
>>>runtime-practical reason?
>>
>>As others have mentioned, it allows for some runtime optimization.
>>
>>As for the intent reflected, I think it's orthogonality with argument
>>passing. Consider that it isn't really necessary to pass multiple
>>arguments in separate variables -- all functions could take a single
>>argument, and if you needed to pass multiple arguments you could pass a
>>list, which the function would take apart to get the individual arguments.
>
>I think the intent reflects a concern with symmetry
>rather than orthogonality.

Yes, that's the adjective I meant to use.

> Orthogonality implies that
>multiple-values brings something new to the
>expressiveness table, and it doesn't.
>
>>But in fact, we don't require programmers to package up multiple input
>>values that way. So why should we require them to do so for multiple
>>output values?
>
>Packing with VALUES isn't any easier than packing with
>LIST, and unpacking with CALL-WITH-VALUES isn't any
>easier than unpacking with APPLY. There is no
>lightening of burden.

True. I suspect a part of the intent was also that people saw Common
Lisp's multiple values feature and wanted it. But Scheme's MV mechanism is
missing some of CL's features. One of the most significant differences in
CL is that extra values can easily be ignored when the caller isn't
interested in them. For instance, the FLOOR function returns both the
floor of the division *and* the remainder, but most often the caller is
only interested in the floor, and it can be treated as a single-valued
function: (print (floor 10 3)) will print just 3 (the remainder 1 is
discarded). Because of this, multiple values are often used for
"auxiliary" data that is frequently uninteresting, and a special mechanism
(such as MULTIPLE-VALUE-BIND) must be used to receive them.

Since Scheme requires all uses of 'values' to be processed by
'call-with-values', this style is not possible. This fits in with Scheme's
philosophy, but it reduces the usefulness of the MV mechanism, IMHO. As
you say, it makes values/call-with-values semantically equivalent to
list/apply.

### Barry Margolin

2002/04/09 16:33:072002/04/09
To:
In article <a8vhb2$jdk$0...@216.39.136.5>,

Ben Goetter <goe...@mazama.net.xyz> wrote:
>2. An abiding affection for first-class values (little-v).

Why an affection for first-class values, but not for first-class arguments?

I think there are languages (probably in the functional language family)
that implement the symmetry by having only single-argument functions, and
if you need to pass multiple arguments you package them up into a
first-class tuple data structure. If you have an abiding affection for
first-class-ness, this is probably the way to go.

### Thant Tessman

2002/04/09 17:49:492002/04/09
To:
Barry Margolin wrote:
> In article <a8vhb2$jdk$0...@216.39.136.5>,
> Ben Goetter <goe...@mazama.net.xyz> wrote:
>
>>2. An abiding affection for first-class values (little-v).
>>
>
> Why an affection for first-class values, but not for first-class arguments?
>
> I think there are languages (probably in the functional language family)
> that implement the symmetry by having only single-argument functions, and
> if you need to pass multiple arguments you package them up into a
> first-class tuple data structure. If you have an abiding affection for
> first-class-ness, this is probably the way to go.

SML for one works this way. It's a good thing.

What would Scheme look like if it made no distinction between passing a
list as a single argument and passing many arguments? I already think of
the distinction as somehow syntactic--that is, arguments are already in
list notation. They're just sort of unquoted in some sense in the

-thant

### Jeffrey Siegal

2002/04/09 17:57:162002/04/09
To:
Barry Margolin wrote:
> As others have mentioned, it allows for some runtime optimization.

I wouldn't say it "allows" optimization, since optimization is always
allowed. It might make optmization somewhat easier, but even that seems
questionable.

### Brian Harvey

2002/04/09 18:18:072002/04/09
To:
Barry Margolin <bar...@genuity.net> writes:
>As for the intent reflected, I think it's orthogonality with argument
>passing. Consider that it isn't really necessary to pass multiple
>arguments in separate variables -- all functions could take a single
>argument, and if you needed to pass multiple arguments you could pass a
>list, which the function would take apart to get the individual arguments.

This criterion of elegance can be pushed even farther -- why bother
distinguishing arguments from return values? Mathematically, a function
is just a special case of a relation, and in the realm of programming
languages this perspective is taken by Prolog.

But the tradition of functions as things with multiple arguments and
one return value is about 1000 years older than this more abstract
definition (and even more older than the one-argument-function lambda
calculus), and it clearly (imho) captures something really useful
and rich, even today. Multiple-return-value functions don't feel like
functions to me.

### Bruce Hoult

2002/04/09 20:55:432002/04/09
To:
In article <3CB361FD...@acm.org>, Thant Tessman <th...@acm.org>
wrote:

> > I think there are languages (probably in the functional language family)
> > that implement the symmetry by having only single-argument functions, and
> > if you need to pass multiple arguments you package them up into a
> > first-class tuple data structure. If you have an abiding affection for
> > first-class-ness, this is probably the way to go.
>
> SML for one works this way. It's a good thing.

*ML allows you to pass arguments as first class tuples, but it's more
common to *not* do this, but to instead have each function take a single
argument and return another function that is then applied to the next
argument.

(lambda (a)
(lambda (b)
(lambda (c)
(lambda (x)
(+ (* a x x) (* b x) c))))))

=> 123

*ML has syntactic sugar so you can just write something like...

fn quadratic a b c x = a * x * x + b * x + c;;

=> 123

... but the above Scheme is what it really means.

-- Bruce

### Matthias Blume

2002/04/09 21:29:522002/04/09
To:
Bruce Hoult <br...@hoult.org> writes:

> In article <3CB361FD...@acm.org>, Thant Tessman <th...@acm.org>
> wrote:
>
> > > I think there are languages (probably in the functional language family)
> > > that implement the symmetry by having only single-argument functions, and
> > > if you need to pass multiple arguments you package them up into a
> > > first-class tuple data structure. If you have an abiding affection for
> > > first-class-ness, this is probably the way to go.
> >
> > SML for one works this way. It's a good thing.
>
> *ML allows you to pass arguments as first class tuples, but it's more
> common to *not* do this, but to instead have each function take a single

It is more common if you ask an OCaml person, it is less common if you
ask an SML person. There are good reasons for the difference. Those
have to do with other language properties (in particular:
left-to-right evaluation, which in the general case makes currying
more expensive).

--
-Matthias

### Matthias Blume

2002/04/09 21:17:342002/04/09
To:
MJ Ray <markj...@cloaked.freeserve.co.uk> writes:

Because it is true. (Multiple values with nearly the same API were already in the
language via functions LIST and APPLY. A good compiler could recognize the idiom and
optimize it away in most cases.)

Actually, along the same line one can say that (second-class) multiple
arguments are a hack. I, in some twisted way, have to agree with
people who say that once you let one such hack in you might as well
let the other in, too. Personally, I have come to prefer languages
that lets in neither, i.e., strict single-argument single-result
functions, with a first-class tupling mechanisms that give you the
expressive power of multiple arguments/results. Static analysis
(e.g., type checking) can often effectively be used to eliminate the

Interestingly enough, the dual approach with multiple
arguments/multiple values a la Scheme has its own inefficiencies that
would require similar analyses to get rid of. Consider function composition:

The straightforward

(define compose (lambda (f g) (lambda (x) (f (g x)))))

does not work with f's and g's that take multiple arguments or produce multiple results.
A correct version of compose is this:

(define compose
(lambda (f g)
(lambda args (call-with-values (lambda () (apply g args)) f))))

The problem is that it has to build its own seemingly superfluous argument "tuple"
represented by the ARGS rest-argument list.

--
-Matthias

### Matthias Blume

2002/04/09 21:22:242002/04/09
To:
markj...@cloaked.freeserve.co.uk (MJ Ray) writes:

You call the above "saving 'noise work'"?!? You gotta be kidding!

Plus, I could already do all this without ary let, car, cdr, or such:

(apply (lambda (v1 v2 v3)

(function-to-process v1 v2)
(another-function v3))

--
-Matthias

### ol...@pobox.com

2002/04/09 21:59:502002/04/09
To:
No doubt multiple return values are highly useful, especially in
writing pure functional code.

However, multiple-values give rise to some difficulties. For example,
the functional identity can no longer be defined as
(define (id x) x)
if we want it to be the unit of the functional composition. Instead,
we have to
(define (id . args) (apply values args))
The functional composition becomes more complex, too.

Oftentimes we wish for a function (trace "some-string" val) that
merely propagates the "val" to its caller. The utility of such a
function is in its side-effect, e.g., printing a message:

(define (trace title arg)
(display title) (newline)
; perhaps print the timestamp as well
arg) ; propagate the arg

The function can be used as
(trace "got request" (get-request)))

This pattern of propagating a value is quite widespread: it occurs in
procedures such as "call-with-input-file" (which must close the port
before passing the value returned by its body) and forms such as
dynamic-wind. The presence of multiple values noticeably complicates
the definitions of such procedures:

(define (trace title . args)
(display title) (newline)
(apply values args))

(call-with-values
get-request
(lambda args
(call-with-values
(lambda () (apply trace (cons title args)))

It is interesting to note that in these situations we often have to
specifically reify multiple values as a list.

The function 'values' itself is more complex than it seems. [in the
following, we mean the function values applied to more or fewer than 1
argument] Perhaps 'values' the only R5RS function that does not yield a
value. The "result" of 'values' is not first-class. BTW, R5RS
specifically says that an "unspecified" result is still a first-class
value. The only other function whose result is not first-class is
'error' (or 'abort', following SRFI-12).

The similarity between 'values' and 'error' runs deeper. One can say
that neither of these functions actually return. That is, they do not
invoke their 'natural' continuation. The function error invokes the
error continuation, which is always present (often implicitly). The
function 'values' invokes a multiple-value continuation, which must be
established by a distinguished 'call-with-values' procedure. The
implementation of 'values' in R5RS corroborates this interpretation.
We can imagine 'values' throwing a special kind of exception, which is
trapped in the handler set by 'call-with-values'. The multiple-values
"exception" is a peculiar one: it can't percolate so it must be caught
right away.

### Joe Marshall

2002/04/09 22:40:062002/04/09
To:

"Bruce Hoult" <br...@hoult.org> wrote in message
news:bruce-7C2967....@copper.ipg.tsnz.net...

>
> *ML allows you to pass arguments as first class tuples, but it's more
> common to *not* do this, but to instead have each function take a single
> argument and return another function that is then applied to the next
> argument.
>
> (lambda (a)
> (lambda (b)
> (lambda (c)
> (lambda (x)
> (+ (* a x x) (* b x) c))))))
>
>
> *ML has syntactic sugar so you can just write something like...
>

Looks more like syntactic curry' to me.

### Eli Barzilay

2002/04/09 22:51:062002/04/09
To:
Matthias Blume <matt...@shimizu-blume.com> writes:

> [...] Personally, I have come to prefer languages that lets in

> neither, i.e., strict single-argument single-result functions, with
> a first-class tupling mechanisms that give you the expressive power
> of multiple arguments/results. Static analysis (e.g., type
> checking) can often effectively be used to eliminate the overhead of
> creating actual tuples.

I don't want to sound like this is c.l.l, but it looks like this
carries zero information -- people who haven't tried it probably don't
know what you're talking about here, and people who do must have some
good reason to use Scheme.

(Personally, I have *really* tried working with ML, but I have come to
realize that no matter how hard I try to do that, a dynamic language
is always more fun to use.)

--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://www.barzilay.org/ Maze is Life!

### Bruce Hoult

2002/04/09 22:56:192002/04/09
To:
In article <aCNs8.17635$%s3.59...@typhoon.ne.ipsvc.net>, "Joe Marshall" <prunes...@attbi.com> wrote: Curry contains sugar. And a little spice... -- Bruce ### Sébastien de Menten 未読、 2002/04/10 3:17:572002/04/10 To: > This criterion of elegance can be pushed even farther -- why bother > distinguishing arguments from return values? Mathematically, a function > is just a special case of a relation, and in the realm of programming > languages this perspective is taken by Prolog. > Loosely speaking, a function f is a relation between elements of two different sets (domain(f) and image(f)) with the particularity that each element of the first set (x in domain(f)) is related to only_one element of the other set ( f(x) in image(f)). > But the tradition of functions as things with multiple arguments and > one return value is about 1000 years older than this more abstract > definition (and even more older than the one-argument-function lambda > calculus), and it clearly (imho) captures something really useful > and rich, even today. Multiple-return-value functions don't feel like > functions to me. Multiple return value is still a function but with a composed set image(f). For instance, f = (lambda (x1 x2 x3) (values (* 2 x1) (/ x2 x3)) is just a function with : - domain(f) = SCM^3 - image(f)=SCM^2 with SCM being the set of scheme values. But for each input in domain(f), it returns one output in image(f). Hence, - if f returns a single composed value for each input => f is a function (the case with the "Multiple-return-value functions") - if f returns a list of values (being composed or not) => f is not a function but a more general relation (f doesn't feel like a function because it isn't). In this case, the returned list can be of different size at each call. This may not be useful at first sight but is heavely used in relational databases. Anyway, I find the multiplevalue stuff mathematically nice but its syntax is ugly. What would be nice is to be able to write: (define (f x y z) ... (values v w)) (define (g x y) ... (values s)) with (g (f 3 4 5)) or (g 4 6) being to possible calls ### MJ Ray 未読、 2002/04/09 19:59:222002/04/09 To: Dorai Sitaram <ds...@goldshoe.gte.com> wrote: > There is already the procedure apply that can do the > unpacking without let, car, cdr, list-ref, etc. I always seem to take a performance hit when using apply. Has anyone got benchmarks of the two ways round? ### MJ Ray 未読、 2002/04/09 19:57:172002/04/09 To: George Caswell <sieg...@attbi.com> wrote: > I did explain my current (limited) understanding of multiple return > values. About the only reason I could fathom for its existence was in > order to make the representation match the intent. I humbly suggest that, > in order to make your responses useful, you pay attention to what you're > reading. I did not understand your attempted explanation (which looks to me like part of a sentence), so I did not try to answer that part. Now that I see you seem to enjoy abusing those whose replies do not meet your exacting completeness standards, I regret following up at all. I will try not to make that error again and humbly apologise for offending you. ### felix 未読、 2002/04/10 5:53:102002/04/10 To: MJ Ray wrote in message ... There is a paper by Kent Dybvig ("Efficient Implementation of Multiple Return Values in Scheme" (or so)), which compares the performance of different programming styles (values, CPS, lists, etc.). Should be available at readscheme.org. Brad Lucier reported timings for a benchmark of the SRFI-25 reference implementation where native multiple values[*] showed to be about 5 times faster than multiple values simulated with lists/apply cheers, felix -- [*] with a preliminary release of Gambit (4.0) ### felix 未読、 2002/04/10 5:59:002002/04/10 To: Jeffrey Siegal wrote in message <3CB363BC...@quiotix.com>... I do not understand. Do you mean optimizing for multiple values is not automatically easier than optimizing for a list-hack? I don't think so (see Dybvig's paper). cheers, felix ### felix 未読、 2002/04/10 6:08:562002/04/10 To: Matthias Blume wrote in message ... >markj...@cloaked.freeserve.co.uk (MJ Ray) writes: > >> Let me have another go. I do not see a problem with saying something like: >> >> (call-with-values (lambda () (function-returning-3values params)) >> (lambda (v1 v2 v3) >> (function-to-process v1 v2) >> (another-function v3))) >> >> In fact, when it fits the situation well, it saves some "noise work" with >> let, car, cdr, list-ref etc and allows the real method to shine through. My >> Opinion Only. > >You call the above "saving 'noise work'"?!? You gotta be kidding! > >Plus, I could already do all this without ary let, car, cdr, or such: > > (apply (lambda (v1 v2 v3) > (function-to-process v1 v2) > (another-function v3)) > (function-returning-3values-using-LIST-instead-of-VALUES params)) > Since call-with-values is just a building-block, one can just as well say: (let-values ([(v1 v2 v3) (function-returning-3values params)]) (function-to-process v1 v2) (another-function v3) ) Less noise, looks better, makes the intention clear, doesn't need a magic compiler and is efficient (by using the native call-with-values). cheers, felix ### Matthias Blume 未読、 2002/04/10 7:29:362002/04/10 To: "felix" <felixu...@freenet.de> writes: Well, that just makes the point very clearly: multiple values are a *performance* hack. -- -Matthias ### Matthias Blume 未読、 2002/04/10 7:35:012002/04/10 To: Eli Barzilay <e...@barzilay.org> writes: > Matthias Blume <matt...@shimizu-blume.com> writes: > > > [...] Personally, I have come to prefer languages that lets in > > neither, i.e., strict single-argument single-result functions, with > > a first-class tupling mechanisms that give you the expressive power > > of multiple arguments/results. Static analysis (e.g., type > > checking) can often effectively be used to eliminate the overhead of > > creating actual tuples. > > I don't want to sound like this is c.l.l, but it looks like this > carries zero information -- people who haven't tried it probably don't > know what you're talking about here, and people who do must have some > good reason to use Scheme. I'd prefer it if you'd speak for yourself only. I am pretty sure that there are people who do know what I am talking about. The above isn't that difficult to understand. (If you think that I simply made a thinly-veiled reference to ML and want the above to be read "I prefer ML", then you are wrong. I intentionally did not mention the concrete instance ML of the broader class of languages that I would prefer as far as their handling of arguments and return values of functions is concerned.) > (Personally, I have *really* tried working with ML, but I have come to > realize that no matter how hard I try to do that, a dynamic language > is always more fun to use.) Well, different strokes for different folks. Anyway, this was not at all about static vs. dynamic, so I don't quite see why you need to bring it up. -- -Matthias ### Eli Barzilay 未読、 2002/04/10 9:52:142002/04/10 To: Matthias Blume <matt...@shimizu-blume.com> writes: > > > [...] Personally, I have come to prefer languages that lets in > > > neither, i.e., strict single-argument single-result functions, > > > with a first-class tupling mechanisms that give you the > > > expressive power of multiple arguments/results. Static analysis > > > (e.g., type checking) can often effectively be used to eliminate > > > the overhead of creating actual tuples. > > I'd prefer it if you'd speak for yourself only. You did not even mention pattern matching to destruct the touple. (And my guess was that anyone who'll know how static analysis can prevent creating these touples belongs to that crowd that knows this already.) > (If you think that I simply made a thinly-veiled reference to ML and > want the above to be read "I prefer ML", then you are wrong. Substitute ML' with languages that have only strict single-argument single-result functions' and that use Static analysis to eliminate overhead'. The ML reference was my theoretical mistake that came out of the practical reality where you posted something about ML in the same neighborhood. But all this is pointless meta-junk, the only thing I wanted to say is exactly this: > Well, different strokes for different folks. ... so what's the point of tempting people to reduce the S/N ratio? > Anyway, this was not at all about static vs. dynamic, so I don't > quite see why you need to bring it up. (Yeah well, the standard approach would be to say that: if multiple return values are a hack, but without them you have a non-symmetrical situation that pushes you to forbid multiple inputs, leading to unbearable Scheme overhead for almost every function call, which can be fixed by using static type analysis and pattern matching, then my reply is that giving up dynamisms that will follow is a way too high price. etc etc etc.) ### Matthias Blume 未読、 2002/04/10 10:58:432002/04/10 To: Eli Barzilay <e...@barzilay.org> writes: > Matthias Blume <matt...@shimizu-blume.com> writes: > > > > > [...] Personally, I have come to prefer languages that lets in > > > > neither, i.e., strict single-argument single-result functions, > > > > with a first-class tupling mechanisms that give you the > > > > expressive power of multiple arguments/results. Static analysis > > > > (e.g., type checking) can often effectively be used to eliminate > > > > the overhead of creating actual tuples. > > > > I'd prefer it if you'd speak for yourself only. > > You did not even mention pattern matching to destruct the touple. > (And my guess was that anyone who'll know how static analysis can > prevent creating these touples belongs to that crowd that knows this > already.) Strictly speaking, you don't need any pattern matching. In this case it would just be syntactic sugar. For conciseness of notation, it would definitely help, though. > > (If you think that I simply made a thinly-veiled reference to ML and > > want the above to be read "I prefer ML", then you are wrong. > > Substitute ML' with languages that have only strict single-argument > single-result functions' and that use Static analysis to eliminate > overhead'. Such languages have a bit of an easier time with first-class multiple values because they *already* do type analysis for other reasons and therefore can piggy-back on that. But the analysis used in, e.g., Scheme, does not have to be a type analysis in the usual sense. Instead, it could be some fairly straightforward CFA, or soft typing, etc. (Actually, those really all boil down to the same thing: CFA.) > > Well, different strokes for different folks. > > ... so what's the point of tempting people to reduce the S/N ratio? The point is that call-with-values and values are "features piled on top of features" -- something that does not give you any additional expressive power, in fact, something that is strictly less expressive than *another* language feature that is *already* in the language. The only thing it has going for itself is the fact that naive implementations can optimize it a bit more easily -- hence the word "performance hack". As I have showed in a previous article, there are situations where the hack actually amounts to a pessimisation. In terms of pragmatics, it is definitely a pessimisation because I need to contort all kinds of otherwise clean code to account for the possibility of multiple values. I do not understand why you think that discussing this is reducing the S/N ratio. CWV and VALUES go very much against the spirit of the design principles that Scheme is so proud of. > > Anyway, this was not at all about static vs. dynamic, so I don't > > quite see why you need to bring it up. > > (Yeah well, the standard approach would be to say that: if multiple > return values are a hack, but without them you have a non-symmetrical > situation that pushes you to forbid multiple inputs, leading to > unbearable Scheme overhead for almost every function call, which can > be fixed by using static type analysis and pattern matching, It can be fixed by a clever compiler, doing *some* kind of compile time analysis. Compilers (even -- or should I say, *especially* those for Scheme) do all kinds of other clever static analyses, too. Static typing at the level of the surface language is not required for this (although it helps if you already have it). Pattern matching has absolutely *nothing* to do with performance one way or the other here. By the way, the "unbearable" overhead does not have to be all that "unbearable" even in an implementation that does not do any sophisticated static analysis: Suppose we have the following semantics of abstraction and application: 1. Abstraction: (LAMBDA (x) <body>) -- takes the function's single argument, binds it to X and evaluates <body> (LAMBDA () <body>) -- takes the function's single argument which must (dynamically) be of type "tuple" with zero components (aka "unit"). Evaluate <body>. (LAMBDA (x1 ... xN) <body>) -- takes the function's single argument which must be of type "tuple" with N components. Bind component i to xi for all i \in 1 .. N. Evaluate <body>. If we assume that there is a form expressing projection for tuples, say (project <tuple> <i>) gives you the <i>th component of <tuple>, then the second form is syntactic sugar for (LAMBDA (arg) <check type of arg to be 0-tuple> <body>) and the last form is syntactic sugar for (LAMBDA (arg) <check type of arg to be N-tuple> (let ((x1 (project arg 1)) ... (xN (project arg N))) <body>)) Notice that techinally all LAMBDA forms evaluate to procedures taking a single argument. 2. Application: (<function> <arg>) -- evaluates <function> and <arg>, <function> must be a procedure, <arg> can be anything. Call <function> with <arg> as its single argument. (<function>) -- evaluates <function>, result must be a procedure expecting a "unit" (see above) argument. Call it with a unit value (empty tuple) as its single argument. (<function> <arg1> ... <argN>) -- evaluates <function> and all <argi>. <function> must be a procedure taking an N-tuple, bundle up <argi> as such an N-tuple, pass it to the procedure as its single argument. If we assume that there is a form expressing tupling, say (tuple <x1> ... <xN>) for N >= 0, then the second form is syntactic sugar for (<function> (tuple)) and the last form is syntactic sugar for (<function> (tuple <arg1> ... <argN>)) If we further assume that (tuple <x>) simply returns <x>, then all three forms can be seens as instances of the same kind of syntactic sugaring. Obviously, the first two forms of application are as cheap (or very nearly so) as those in any existing Scheme implementation. The potential performance hit comes with the third form. So here is what we do: Instead of creating the tuple in the caller, we pass the values <arg1> ... <argN> in "unpacked" form (e.g., as individual stack slots, or even in registers), just like it is done currently in Scheme. In addition, as any prudent Scheme implementation must do, we also say how big N actually is. (Current Scheme implementations must do the latter, too, in order to be able to detect arity mismatches dynamically.) Now, on the callee side, we proceed as follows: In the (LAMBDA (x) <body>) case, we check to see if N = 1. If so, we simply take the value, bind it to x, and continue with <body> In the case of N = 0, we bind x to an empty tuple and evaluate <body>. In the case of N > 0, we now tuple up the arguments, bind the resulting tuple to x, and then continue. Notice that the only expensive case here is the one that would be considered a dynamic type error in existing Schemes. In the (LAMBDA () <body>) case, we check to see if N = 0. If so, we proceed with <body>. In all other cases we raise an error. In the (LAMBDA (x1 ... xM) <body>) case, we check to see if: M = N => bind x1 ... xN as usual and proceed with <body> N = 1 => check the type of the actual argument. It must be a tuple of size M. If so, unpack the tuple, bind x1 ... xM, and proceed. If not, raise an error. otherwise => raise error Again, the only expensive case occurs where traditional Schemes would raise an error anyway. The point of all this is that from the point of the semantics of the surface language, multiple values (for both arguments and results) are good old-fashioned first-class values like everything else in Scheme. And to get good performance on the critical path case where first-class-ness is not semantically required, we do the actual tupling lazily, using more efficient parameter-passing mechanisms wherever possible. In other words, there really is no excuse for having CALL-WITH-VALUES and VALUES in a language. > then my reply is that giving up dynamisms that will follow is a way > too high price. etc etc etc.) Calm down. Nobody asked you to give up your precious dynamisms. :-) -Matthias ### George Caswell 未読、 2002/04/10 11:04:342002/04/10 To: Apology accepted. More seriously: This is just an ongoing pet peeve of mine - someone asks for a question to be answered, and the answer is of the form of "why would you do that?" or "why are you asking that question?" Something pretty much orthogonal to the question being asked. Answers like this are noise. In this case I asked why (values) exists. Your first answer was along the lines of "You can turn values into a list", and IMO did not at all address the question. Your second answer was that you felt it was more semantically clean than using lists. The second answer did address the question, and I appreciate the input. Anyway, thanks to everybody who helped me out with this. ---GEC New projects page: http://home.attbi.com/~sieg_haro/ (M-x depeche-mode) "Must... Finish... Zaku..." ### Matthias Blume 未読、 2002/04/10 11:04:212002/04/10 To: Matthias Blume <matt...@shimizu-blume.com> writes: > In the (LAMBDA () <body>) case, we check to see if N = 0. If so, > we proceed with <body>. In all other cases we raise an error. Sorry, got this one wrong. Should have said: In the (LAMBDA () <body>) case, we check to see if N = 0. If so, we proceed with <body>. If N = 1, we check the type of the single argument, which must be "unit". If so, we proceed with <body>. Otherwise, we raise an error. If N > 1, we raise an error. -- -Matthias ### Eli Barzilay 未読、 2002/04/10 11:57:192002/04/10 To: Matthias Blume <matt...@shimizu-blume.com> writes: > The point is that call-with-values and values are "features piled on > top of features" -- something that does not give you any additional > expressive power, in fact, something that is strictly less > expressive than *another* language feature that is *already* in the > language. [...] > In terms of pragmatics, it is definitely a pessimisation because I > need to contort all kinds of otherwise clean code to account for the > possibility of multiple values. At this point I have to admit that I am pretty much leaning towards this opinion. The only thing that I found useful with multiple values is in case you want to define several functions sharing a single (hidden) state without using a redundant binding as in (define f+g (let ((state ...)) (let ((f ...) (g ...)) (list f g)))) (define f (car f+g)) (define g (cadr f+g)) but this needs some form of define-values'. > I do not understand why you think that discussing this is reducing > the S/N ratio. [...] Well discussing *this* is certainly bringing it up -- it's those "I can do X if I had feature Y in my language", for any property Y that is far enough from Scheme to be impractical or even something to learn from. So your current post is definitely good stuff, compared to the other. (You probably think that this one is just being more verbose...) > Suppose we have the following semantics of abstraction and application: > 1. Abstraction: > [...] > 2. Application: > [...] > If we further assume that (tuple <x>) simply returns <x>, then > all three forms can be seens as instances of the same kind of > syntactic sugaring. > [...] So here is what we do: Instead of creating the tuple in the > caller, we pass the values <arg1> ... <argN> in "unpacked" form > (e.g., as individual stack slots, or even in registers), just like > it is done currently in Scheme. In addition, as any prudent Scheme > implementation must do, we also say how big N actually is. (Current > Scheme implementations must do the latter, too, in order to be able > to detect arity mismatches dynamically.) Yes (just need to make the above a bit more complex to allow variable number of arguments), but after you've done all this, you end up with something which looks exactly like the current state of things without multiple values, which you could add to reify these tuples. And maybe then add a form that will be a function application using a user-supplied tuple, as with CL's multiple-value-call': (multiple-value-call f (values ...)) Do you see any practical result of your description which makes things different than where they are now? > Calm down. Nobody asked you to give up your precious dynamisms. :-) Whew... (Semi-seriously, I think it's good to worry about this, there's no doubt static languages have advantages, so I think it's good to have a reality check every once in a while... The easiness of expressing any argument structure in ML with minimized performance penalty is certainly one of these attractive features. (I even implemented a pattern matcher as a result.)) Disclaimer: it is getting very late for me (in contrast to the local timezone), so I apologize in advance if there's anything obviously stupid in the above... Time to crash now... ### ozan s yigit 未読、 2002/04/10 11:59:062002/04/10 To: Matthias Blume <matt...@shimizu-blume.com> writes: > Well, that just makes the point very clearly: multiple values > are a *performance* hack. it is a *programmer* performance hack. oz -- you take a banana, you get a lunar landscape. -- j. van wijk ### ozan s yigit 未読、 2002/04/10 11:45:262002/04/10 To: Barry Margolin <bar...@genuity.net> writes: > But in fact, we don't require programmers to package up multiple input > values that way. So why should we require them to do so for multiple > output values? this is the crux of it. it also makes it less clumsy, easier to understand less error-prone etc etc. multiple return values are not a luxury. oz --- the power of the unaided human mind is highly overrated. -- Donald Norman ### Barry Margolin 未読、 2002/04/10 12:04:122002/04/10 To: In article <a8vpav$mkn\$1...@abbenay.CS.Berkeley.EDU>,

Brian Harvey <b...@abbenay.cs.berkeley.edu> wrote:
>But the tradition of functions as things with multiple arguments and
>one return value is about 1000 years older than this more abstract
>definition

Really? Didn't many early programming languages, such as Algol, allow you
to specify IN, OUT, and IN-OUT parameters to procedures? Multiple values
allows you to get an effect analogous to multiple OUT parameters.

It's true that when procedures are used as functions in expressions,
there's a distinguished return value, and it's traditionally been a
singleton (so that it can be substituted into the expression). In some
languages a function can assign to its OUT parameters while also returning
a value, but the Lisp family doesn't support this. So multiple return
values were introduced to fill the gap. As I mentioned in another post,
Common Lisp recognizes the special status of the primary value when a
function call appears in an ordinary expression, by ignoring any extra
values.

--
Barry Margolin, bar...@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

### Marc Feeley

2002/04/10 12:08:102002/04/10
To:
> > Brad Lucier reported timings for a benchmark of the SRFI-25 reference
> > implementation where native multiple values[*] showed to be about 5 times
> > faster than multiple values simulated with lists/apply
>
> Well, that just makes the point very clearly: multiple values are a *performance* hack.

I should point out that the implementation of multiple values in
Gambit is really straightforward. It simply consists in packaging the
values in a specially tagged vector (except when there is a single
value). I think the factor of 5 performance improvement observed is
simply due to the inlining of the allocator. So you could get the
same performance with (vector value1 value2 ...) if you use the right
declarations for this call to be inlined.

I don't like multiple values, but they are supported in Gambit 4.0
for compatibility with R5RS.

Marc

### Dorai Sitaram

2002/04/10 12:35:582002/04/10
To:
In article <vi4it6z...@blue.cs.yorku.ca>,

ozan s yigit <o...@blue.cs.yorku.ca> wrote:
>Matthias Blume <matt...@shimizu-blume.com> writes:
>
>> Well, that just makes the point very clearly: multiple values
>> are a *performance* hack.
>
>it is a *programmer* performance hack.

OK, I guess you have some examples and want us to ask.

Give a couple of examples.

### Matthias Blume

2002/04/10 12:32:252002/04/10
To:
Eli Barzilay <e...@barzilay.org> writes:

> Do you see any practical result of your description which makes things
> different than where they are now?

Absolutely. For starters, I can write "compose" the way I wanted it:

(define ((compose f g) x) (f (g x)))

This handles all cases, including g or f taking or produces multiple values.

Since multiple values are now first-class values, you can even do things like

(map f (map g <list>))

where g might return multiple values. The intermediate list would
nicely collect them as first-class tuples and dispatch the
(potentially) multiple-arg f to work on those. Imagine the
contortions that you'd need in current Schemes.

Let's try, assuming that multiple args for g are given as sublists
of <list>, and that multiple results of f are to be collected as
sublists of the final result:

(map (lambda (args)
(call-with-values (lambda () (apply f args)) list))
(map (lambda (args)
(call-with-values (lambda () (apply g args)) list))
<list>))

(Don't sue me if I didn't get this right. It would only make my point
-- namely being awkward beyond reason.)

-Matthias

### Joe English

2002/04/10 12:27:422002/04/10
To:
Matthias Blume wrote:

>"felix" writes:
>> There is a paper by Kent Dybvig ("Efficient Implementation of Multiple
>> Return Values in Scheme" (or so)), which compares the performance
>> of different programming styles (values, CPS, lists, etc.). Should
>>
>> Brad Lucier reported timings for a benchmark of the SRFI-25 reference
>> implementation where native multiple values[*] showed to be about 5 times
>> faster than multiple values simulated with lists/apply
>
>Well, that just makes the point very clearly: multiple values are a
>*performance* hack.

What's always puzzled me is that in Ashley & Dybvig's paper,
the only way they were able to actually get increased performance
is to transform things like

(call-with-values
(lambda () producer)
(lambda (v1 v2 ... vN) consumer))

into:

(mv-let producer (v1 v2 ... vN) consumer)

where mv-let (and mv-call) are implemented as primitives.
Performing this transformation is complicated by the fact
that call-with-values is an ordinary procedure, not syntax.

Further, the code I've seen written to demonstrate the utility
of multiple return values almost invariably defines macros to
provide Lisp-style (multiple-value-bind) and (multiple-value-call)
as syntactic sugar for (call-with-values).

I really wonder why R5RS specified (call-with-values) as the
essential primitive instead of (mv-let) and (mv-call), when
the former is too ugly to be used directly by programmers
and too awkward to be implemented efficiently without first
transforming it into the latter. Was there a moratorium on
introducing new special forms in effect at the time, or what?

--Joe English

### Erann Gat

2002/04/10 12:21:152002/04/10
To:
In article <fo1ydnb...@trex10.cs.bell-labs.com>, Matthias Blume
<matt...@shimizu-blume.com> wrote:

> Suppose we have the following semantics of abstraction and application:
>
> 1. Abstraction:
>
> (LAMBDA (x) <body>) -- takes the function's single argument, binds
> it to X and evaluates <body>

Did you mean (lambda x body)? The design as you presented it changes the
semantics of Scheme, e.g.: ((lambda (x) x) 1 2) in Scheme is an error but
in your semantics is (tuple 1 2).

Your proposed semantics also require a tuple of one element to be eq to
the element, which seems very weird to me.

E.

### Hrvoje Blazevic

2002/04/10 6:05:062002/04/10
To:
Marc Feeley wrote:
>
> I don't like multiple values, but they are supported in Gambit 4.0
> for compatibility with R5RS.
>

Talking of Gambit 4.0; where is it?

### Matthias Blume

2002/04/10 13:31:382002/04/10
To:
g...@jpl.nasa.gov (Erann Gat) writes:

> In article <fo1ydnb...@trex10.cs.bell-labs.com>, Matthias Blume
> <matt...@shimizu-blume.com> wrote:
>
> > Suppose we have the following semantics of abstraction and application:
> >
> > 1. Abstraction:
> >
> > (LAMBDA (x) <body>) -- takes the function's single argument, binds
> > it to X and evaluates <body>
>
> Did you mean (lambda x body)? The design as you presented it changes the
> semantics of Scheme, e.g.: ((lambda (x) x) 1 2) in Scheme is an error but
> in your semantics is (tuple 1 2).

Right. My design does change the semantics. I am aware of that. (And I believe
the change would be for the better -- if there wouldn't be that nasty littly thing
called legacy code. Not a big deal in the case of Scheme, though. IMHMO.)

> Your proposed semantics also require a tuple of one element to be eq to
> the element, which seems very weird to me.

Why weird? Other languages do that, too. It works very well in practice.

--
-Matthias

### Matthias Blume

2002/04/10 14:34:512002/04/10
To:
Eli Barzilay <e...@barzilay.org> writes:

> Yes (just need to make the above a bit more complex to allow variable

> number of arguments), [ ... ]

It's already covered. All you need is something that explicitly
manipulates tuple values. I already mentioned PROJECT and TUPLE. If
you also have TUPLE-SIZE and perhaps TUPLE? then you can handle
multiple arguments easily:

(define *
(lambda (args)
(if (tuple? args)
(do ((i 0 (+ i 1))
(res 1 (times2 res (project args i))))
((>= i (tuple-size args)) res))
(if (number? args)
args
(error "numeric argument(s) required for *")))))

Notice, by the way, that TUPLE, TUPLE?, TUPLE-SIZE, and PROJECT behave
remarkably like VECTOR, VECTOR?, VECTOR-LENGTH, and VECTOR-REF. So
existing implementations would not even have to add a new type. The
only thing required would be to implement lazy argument tupling
("vectoring") the way I described.

Anyway, since I don't see this happening to Scheme anytime soon, we
can leave it at this.

--
-Matthias

### Eli Barzilay

2002/04/10 14:50:362002/04/10
To:
Matthias Blume <matt...@shimizu-blume.com> writes:

> Eli Barzilay <e...@barzilay.org> writes:
>
> > Do you see any practical result of your description which makes things
> > different than where they are now?
>
> Absolutely. For starters, I can write "compose" the way I wanted it:
>
> (define ((compose f g) x) (f (g x)))

Whow, just one second here...

It might just my (still uncrashed and now fully foggy) brain, but I
still don't see the difference -- if you want the above to work with
multiple value, then you have to always apply functions on one
argument, so now I have to do

(+ (values 1 2 (* (values 3 4))))

if this is your intention, then your point must be that it *could*
have been possible to treat multiple arguments the same way ML does
it -- and in that case I agree that it is possible, but you have to go
down the path of:

* Since every function gets a single arguments, then it is not really
necessary to have parens for applications, you could use them for

+(1 2 (*(3 4)))

* The second parens are necessary to avoid *' being an argument, so
to make it easier just put commas between arguments:

+(1, 2, *(3, 4))

and this goes in an obvious direction...

### Brian Harvey

2002/04/10 15:23:012002/04/10
To:
Barry Margolin <bar...@genuity.net> writes:
>>But the tradition of functions as things with multiple arguments and
>>one return value is about 1000 years older than this more abstract
>>definition
>
>Really? Didn't many early programming languages, such as Algol, allow you
>to specify IN, OUT, and IN-OUT parameters to procedures?

Algol (and computers altogether) are about 1000 years younger than
functions. My point is that there's something about the idea of the
(one-return-value) function that's caused it to live forever, and
I'm suggesting that it's nice to have programming languages that model
this extremely successful idea.

### Matthias Blume

2002/04/10 15:25:222002/04/10
To:
Eli Barzilay <e...@barzilay.org> writes:

> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Eli Barzilay <e...@barzilay.org> writes:
> >
> > > Do you see any practical result of your description which makes things
> > > different than where they are now?
> >
> > Absolutely. For starters, I can write "compose" the way I wanted it:
> >
> > (define ((compose f g) x) (f (g x)))
>
> Whow, just one second here...
>
> It might just my (still uncrashed and now fully foggy) brain, but I
> still don't see the difference -- if you want the above to work with
> multiple value, then you have to always apply functions on one
> argument, so now I have to do
>
> (+ (values 1 2 (* (values 3 4))))

No, the point was that even though internally this is what's going on
(at least at an abstract level), we still write

(+ 1 2 (* 3 4))

which is then treated as syntactic sugar for what you wrote.

> * Since every function gets a single arguments, then it is not really
> necessary to have parens for applications, you could use them for
> values instead, so just write:
>
> +(1 2 (*(3 4)))
>
> * The second parens are necessary to avoid `*' being an argument, so
> to make it easier just put commas between arguments:
>
> +(1, 2, *(3, 4))
>
> and this goes in an obvious direction...

No, I intentionally did not want to go down this path (at least not in
this discussion :). Basically, what the proposed language did was to
incorporate implicit tupling into the syntax for function application
and implicit pattern-matching into the syntax for LAMBDA. This is to
retain the usual Scheme syntax for most cases even though "under the
hood" we have the one-argument one-result rule. Once this has been
established, we can recover the efficiency of a multiple-argument
Scheme (as we have it now) by doing tupling lazily (i.e., not in the
caller but in the callee -- and only if the callee does not take the
tuple apart immediately again).

So again, there are two things here:

1. a language change that makes "multiple values" into first-class
values (tuples), together with syntactic sugar to hide most of the
common-case introductions and eliminations of such tuples, and
together with the new rule that all functions take a single
argument and produce a single result (which can but does not have to
be one of those first-class tuples)

2. an implementation technique for the so-changed language that is
comparable in efficiency to today's Schemes in those cases that
are expressible in both the new and the old language

--
-Matthias

### Erann Gat

2002/04/10 15:49:512002/04/10
To:
In article <fohemji...@trex8.cs.bell-labs.com>, Matthias Blume
<matt...@shimizu-blume.com> wrote:

It's probably a reflection of my inexperience. The only language I've
used that has tuples is Python, where (1,) != 1.

I think my unease has to do with the fact that I can no longer specify
that a function accepts exactly one argument/value. This:

((lambda () ...) 1) --> ERROR
((lambda (x) ...) 1 2) --> (tuple 1 2)
((lambda (x y) ...) 1 2 3) --> ERROR
((lambda (x y z) ...) 1 2 3 4) --> ERROR
etc.

seems like a gratuitous asymmetry to me. I'd be happier if (lambda (x)
...) meant destructure a single-element tuple, while (lambda x ...) meant
bind x to a single value or a tuple according to the number of arguments.
The asymmetry in the semantics has a matching asymmetry in the syntax, and
it also seems to fit more neatly with the current Scheme Way. The only
thing that changes is that &rest args are packaged up as a tuple instead
of as a list.

But I confess I have not thought this through.

E.

### Barry Margolin

2002/04/10 16:15:252002/04/10