Google グループは Usenet の新規の投稿と購読のサポートを終了しました。過去のコンテンツは引き続き閲覧できます。
Dismiss

Motivations for multiple return values feature?

閲覧: 287 回
最初の未読メッセージにスキップ

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
news:Pine.LNX.3.96.102040...@adamant.attbi.com...

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
been a reason (good, bad... I'm not ready to pass judgement) that it was added
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
your question differently?

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
news:Pine.LNX.3.96.102040...@adamant.attbi.com...

> 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
> your question differently?
>

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

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

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
traditional form.

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

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

((((quadratic 1) 2) 3) 10)

=> 123


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

fn quadratic a b c x = a * x * x + b * x + c;;
quadratic 1 2 3 10;;

=> 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
overhead of creating actual tuples.

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

(function-returning-3values-using-LIST-instead-of-VALUES params))

--
-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
(generate-reply
(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)))
generate-reply)))

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.
>
> (define quadratic
> (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.
So I'll 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
>> 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
>
>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

jeng...@flightlab.com

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

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
To:
In article <fohemji...@trex8.cs.bell-labs.com>,
Matthias Blume <matt...@shimizu-blume.com> wrote:

>g...@jpl.nasa.gov (Erann Gat) writes:
>> 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.

Doesn't it mean that tuples are not first-class objects? If they were,
tuples could be elements of tuples. But then you wouldn't be able to
distinguish (tuple (tuple x y z)) from (tuple x y z).

Barry Margolin

未読、
2002/04/10 15:52:542002/04/10
To:
In article <a923el$nfp$1...@abbenay.CS.Berkeley.EDU>,

Brian Harvey <b...@abbenay.cs.berkeley.edu> wrote:
>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.

Sorry, I thought you were talking about computers, and using "1000 years"
euphemistically (to mean "for as long as there have been programming
languages").

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

If you're talking about mathematical functions, they're defined as mappings
from one set (the domain) to another set (the range), and they only have
one input and one output. If you want more than one argument, the domain
is defined to be a set of tuples or you use currying, and if you want more
than one value, the range is a set of tuples. This is what functional
languages are modeled after. Scheme follows this model for the values, but
not the arguments.

Dorai Sitaram

未読、
2002/04/10 16:37:282002/04/10
To:
In article <qK0t8.25$qt4...@paloalto-snr1.gtei.net>,

Barry Margolin <bar...@genuity.net> wrote:
>In article <a923el$nfp$1...@abbenay.CS.Berkeley.EDU>,
>Brian Harvey <b...@abbenay.cs.berkeley.edu> wrote:
>>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.
>
>Sorry, I thought you were talking about computers, and using "1000 years"
>euphemistically (to mean "for as long as there have been programming
>languages").

"euphemistically"? What's so unpleasant or offensive about
"for as long as there have been programming languages"? :-)

Matthias Blume

未読、
2002/04/10 16:42:202002/04/10
To:
g...@jpl.nasa.gov (Erann Gat) writes:

> In article <fohemji...@trex8.cs.bell-labs.com>, Matthias Blume
> <matt...@shimizu-blume.com> wrote:
>
> > > 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.
>
> 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.

Well, all functions do at all times, so no need to specify it. :-)
Why would you want to say such a thing anyway?

> 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 this would once again break things like COMPOSE -- not breaking of
which was the whole point of the exercise. Think of it this way: There
really is only one kind of function, and (LAMBDA (<var>) <body>) is the
"home syntax" for that. Everything else is just syntactic sugar to
avoid having to write things like

(f (tuple 1 2)) instead of just (f 1 2) or
(f (tuple)) instead of just (f) or

(lambda (tmp) (let ((x (project tmp 1)) (y (project tmp 2))) ...))

instead of just

(lambda (x y) ...)

etc.

The fact that (lambda (x) ...) when called with anything but one syntactic
argument receives a tuple is precisely the *feature* that I am after.

By the way, all is completely symmetric if you accept that (tuple <x>) == <x>.
With this, (lambda (x) ...) can really be thought of as "destructuring" a
one-component tuple. This is precisely the way we do it in math, where
cartesian products with only one factor are typically identified with that
factor.

--
-Matthias

Matthias Blume

未読、
2002/04/10 16:43:592002/04/10
To:
Barry Margolin <bar...@genuity.net> writes:

> In article <fohemji...@trex8.cs.bell-labs.com>,
> Matthias Blume <matt...@shimizu-blume.com> wrote:
> >g...@jpl.nasa.gov (Erann Gat) writes:
> >> 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.
>
> Doesn't it mean that tuples are not first-class objects? If they were,
> tuples could be elements of tuples. But then you wouldn't be able to
> distinguish (tuple (tuple x y z)) from (tuple x y z).

You could, indeed, not distinguish between these two -- because they are
defined to be the same. This has no bearing on whether tuples are first-class.
Or do you say that numbers are not first class just because we cannot
distinguish between (+ 1 2) and (+ (+ 1 2)) ?

--
-Matthias

Brian Harvey

未読、
2002/04/10 16:53:352002/04/10
To:
Barry Margolin <bar...@genuity.net> writes:
>> 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.
>
>If you're talking about mathematical functions, they're defined as mappings
>from one set (the domain) to another set (the range), and they only have
>one input and one output.

No, *that* idea isn't 1000 years old; it's at most 300 years old, and
I think less than that. (My math history is a little fuzzy.) What I'm
saying is that the idea of functions is older than the idea of sets
(and therefore the idea of mappings between sets). I'm trying to gain
some respect for the *naive* notion of function, as distinct from the modern
*formal* definition. The whole terminology of partial derivatives, for
example, is built on top of the naive many-args-one-result view of functions.

And, more recently, Scheme (pre-R5RS) was built on that naive view. I'm
just suggesting that that wasn't a mistake; that the naive view of
functions is really, really powerfully expressive.

(This isn't intended as a *proof* that the set-based definition of function
isn't also a useful programming language construct, although in fact I could
easily live without it for the reasons that others have already mentioned.)

Barry Margolin

未読、
2002/04/10 17:01:532002/04/10
To:
In article <fozo0bu...@trex8.cs.bell-labs.com>,

Those aren't numbers, they're lists. They happen to have the printed
representation of expressions that evaluate to numbers, but the
relationship between the lists and the numbers are coincidental.

The point is that first class objects can be referenced in locations and
they retain their identity. If a tuple is stored in a tuple and then loses
its tuple-ness, it's not first class.

Question: if (tuple (tuple 1 2)) is equivalent to (tuple 1 2), would (tuple
(tuple 1 2) (tuple (3 4))) be equivalent to (tuple 1 2 3 4)? If not, why
not? ISTM that either tuples as top-level elements of tuples should always
be spread, or they shouldn't.

Jeffrey Siegal

未読、
2002/04/10 18:03:512002/04/10
To:
felix wrote:
> Jeffrey Siegal wrote in message <3CB363BC...@quiotix.com>...
>
>>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.
>
> 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).

I didn't say that, but I certainly don't believe it would always be
"automatically" easier. If you have a compiler that optimizes list
operations generally, then it might not require any additional work to
optimize the common idiom of returning a list and then decomposing it in
the caller.

In any case, what I said was that it doesn't allow optimization. It
might facilitate optimization.

Barry Margolin

未読、
2002/04/10 18:29:552002/04/10
To:
In article <3CB4B6C7...@quiotix.com>,

Jeffrey Siegal <j...@quiotix.com> wrote:
>felix wrote:
>> Jeffrey Siegal wrote in message <3CB363BC...@quiotix.com>...
>>
>>>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.
>>
>> 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).
>
>I didn't say that, but I certainly don't believe it would always be
>"automatically" easier. If you have a compiler that optimizes list
>operations generally, then it might not require any additional work to
>optimize the common idiom of returning a list and then decomposing it in
>the caller.

When you're compiling the function that returns the list, you don't know
that the callee is planning on using it as the argument to apply.
Compile-time optimization is only possible if you do block compilation of
the callee and all its callers.

I think that's the difference between returning lists and using values: the
latter can *only* be used to pass to a continuation via call-with-values,
and the compiler can take advantage of that restriction in its code
generation.

I suppose you can do run-time optimization. When calling a function, you
can indicate whether the return value will be used as the last argument to
'apply'. It could even transmit the function being applied and the other
arguments as hidden arguments to the function. Then the tail call to
'list' could be transformed into a tail call to the function being applied.

Matthias Blume

未読、
2002/04/10 22:19:292002/04/10
To:
Barry Margolin <bar...@genuity.net> writes:

> In article <fozo0bu...@trex8.cs.bell-labs.com>,
> Matthias Blume <matt...@shimizu-blume.com> wrote:
> >Barry Margolin <bar...@genuity.net> writes:
> >
> >> In article <fohemji...@trex8.cs.bell-labs.com>,
> >> Matthias Blume <matt...@shimizu-blume.com> wrote:
> >> >g...@jpl.nasa.gov (Erann Gat) writes:
> >> >> 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.
> >>
> >> Doesn't it mean that tuples are not first-class objects? If they were,
> >> tuples could be elements of tuples. But then you wouldn't be able to
> >> distinguish (tuple (tuple x y z)) from (tuple x y z).
> >
> >You could, indeed, not distinguish between these two -- because they are
> >defined to be the same. This has no bearing on whether tuples are first-class.
> >Or do you say that numbers are not first class just because we cannot
> >distinguish between (+ 1 2) and (+ (+ 1 2)) ?
>
> Those aren't numbers, they're lists.

Huh? How is this different from the situation that you gave using tuple
instead of +?

> They happen to have the printed
> representation of expressions that evaluate to numbers, but the
> relationship between the lists and the numbers are coincidental.

... and so is the relationship between the expressions that you gave and
their respective values. When we talk about first-class-ness, we
talk about values.

> The point is that first class objects can be referenced in locations and
> they retain their identity. If a tuple is stored in a tuple and then loses
> its tuple-ness, it's not first class.

This is nonsense. "First-class" means that things are values that can be
passed to functions, returned to functions, and be stored in datastructures.
The notion of "identity" does not come in at all. There are programming
languages where there is no notion of "identity" in the Scheme or Lisp sense,
and yet, there are first-class values.

The expression (tuple <arg>) does not allocate anything but simply returns
its argument, by definition. I do not see how such a definition in any
way compromises first-class-ness.

> Question: if (tuple (tuple 1 2)) is equivalent to (tuple 1 2), would (tuple
> (tuple 1 2) (tuple (3 4))) be equivalent to (tuple 1 2 3 4)?

No.

> If not, why not?

Because that's how I defined the tuple constructor to behave.

> ISTM that either tuples as top-level elements of tuples should always
> be spread, or they shouldn't.

Tuples are never "spread". The only "exception" of sorts is that
one-component tuples are identical to their single component. That's
how things work in math, too.

-Matthias

Al Petrofsky

未読、
2002/04/11 0:34:222002/04/11
To:
jeng...@flightlab.com (Joe English) writes:

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

Something like that. The scheme preference for minimal syntactic
sugar had been in effect for quite some time, with the set of derived
expression types actually shrinking. The most noteworthy example was
the replacement of catch with call/cc.

I'll let someone else explain the reasons for that preference. What
prompts me to post is that while following this debate I came to the
realization that multiple values could be made somewhat less awkward
without adding any syntactic sugar if call-with-values were logically
extended to take additional arguments for passing to the producer. If
it were also given a name of reasonable length, like call/k, then
this:

(call-with-values
(lambda () (foo arg1 arg2))
(lambda (result1 result2)
blah
blah))

would become this:

(call/k foo arg1 arg2
(lambda (result1 result2)
blah
blah))

whose verbosity strikes me as reasonable. With a companion apply/k
procedure, called as (apply/k producer arg ... arglist consumer),
Blume's binary compose becomes:

(define (compose f g)
(lambda x (apply/k g x f)))

For these procedures to be efficient, they need to be provided by the
system, because if you write n-arg call-with-values yourself using
standard 2-arg call-with-values, you will be faced with the
inefficiency of scheme's variable arity interface. I think of that
problem as a separate issue from multiple return values, although
there are proposals like Blume's that address both issues.

-al

Eli Barzilay

未読、
2002/04/11 1:50:512002/04/11
To:
Matthias Blume <matt...@shimizu-blume.com> writes:

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

What about (+ (f) (f)) where f returns two values?

Jeffrey Siegal

未読、
2002/04/11 2:37:322002/04/11
To:
Barry Margolin wrote:
> When you're compiling the function that returns the list, you don't know
> that the callee is planning on using it as the argument to apply.

It isn't clear that you necessarily need to know that.

> Compile-time optimization is only possible if you do block compilation of
> the callee and all its callers.

"Compile-time optimization" is a vague term that could mean any number
of things, including making list (or vector--see below) operations
themselves very efficient. so there isn't any (or much) need to avoid
them. As Marc Feeley pointed out, the major factors making multiple
values in Gambit efficient is inlining the allocation and using a vector
rather than a list (presumably this reduces the number of allocation
operations and the size and time cost of dealing with CDR cells).

Jeffrey Siegal

未読、
2002/04/11 2:40:072002/04/11
To:
Al Petrofsky wrote:
> I'll let someone else explain the reasons for that preference [for avoiding special forms].

I can't explain the reasons for the preference, but I can observe that
it makes analyzing and manipulating programs easier, at least at
superficial level (which is often enough).

Stephan H.M.J. Houben

未読、
2002/04/11 4:50:262002/04/11
To:
In article <folmbva...@blume-pcmh.research.bell-labs.com>,
Matthias Blume wrote:
>"felix" <felixu...@freenet.de> writes:
>
>> MJ Ray wrote in message ...
>> >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?

>>
>> 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
>
>Well, that just makes the point very clearly: multiple values are a *performance* hack.

Well, there are some other reasons for having them:

1. It makes it possible to create continuations that accept an arbitrary
number of arguments, thereby removing a distinction between
continuations and "ordinary" procedures in this regard.

2. It prevents "leakage". "Leakage" is when implementation details of
a procedure are visible to the caller. In the case of returing
a list, the fact that the list is returned is leakage, and more seriously,
the indentity of the list and what happens when the list gets mutated
is leakage.

IMHO, multiple values fits in the general tendency of Scheme to move
towards more abstract types, along the lines of making #f distinct from (),
characters distinct from numbers, and procedures a separate type rather
than a list whose car is lambda.

By using a specialised and restricted, construct, the programmer can
communicate her intentations more unambigously. This primarily
benefits the reader of the program, but in principle the compiler
can also benefit from this information.

Stephan

felix

未読、
2002/04/11 6:42:252002/04/11
To:

Matthias Blume wrote in message ...

>"felix" <felixu...@freenet.de> writes:
>
>> MJ Ray wrote in message ...
>> >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?
>>
>> 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
>
>Well, that just makes the point very clearly: multiple values are a *performance* hack.
>


Depending on your point of view. But one shouldn't forget that
it also clarifies your code (especially when using some macros that wrap
the admittedly verbose `call-with-values' into a somewhat nicer form).
Using intermediate lists and vectors is something *I* would call a "hack".


cheers,
felix


Francois-Rene Rideau

未読、
2002/04/11 8:31:092002/04/11
To:
Let me try to restate the Matthias Blume proposal for having
tuples behave in some Scheme dialect like they do in ML.

One way to explain the idea to Lispers is to tell them that
instead of a one generic n-ary tuple constructor, we instead have
a family of fixed-arity operators tuple/0, tuple/1, tuple/2, ... tuple/n
and a generic function tuple that dispatches over these operators.
NOW, in the ML case, that Matthias recommends for a Lisp dialect,
the case n=1 is special-cased to identity,
and optionally tuple/0 is special-cased to nil '(), #f or a special unit,
while for n>=2 and for n>=2 only is tuple/n some kind of constructor
much like vector (could be vector itself, except that in ML, it's read-only).

This means that tuple is not a constructor for n=1, so that tuple/1 doesn't
return something recognizable by a predicate tuple?, unless its unique
argument is a tuple for n>=2. This can be very confusing for generic programs
using dynamic type introspection, and goes counter to the Lisp tradition.
ML doesn't have any kind of dynamic type introspection, so this is no
problem for ML dialects. Actually, in some logic-oriented ML dialects,
tuples are specified out of cons pairs instead of vectors, and the semantics
are equivalent, thanks to static typing and read-onlyness.

The problem becomes particularly acute because of subtle interactions
induced by this special case for dynamically generated code.
For instance, if a macro-generated program expands to
(tuple-nth n x)
this might suddenly have bizarre effects due to x being a 1-tuple containing
a 2-tuple: and instead of returning the first member of the generic n-tuple
where n happens to be 1 in the generated code, such a generated program
would access the first member of the contained 2-tuple. BAD.
ML doesn't have to cope with macro-generated code, and avoids this issue
at the programmer-level by resorting to static type-checking.
I suppose a Lisp dialect using such tuples could have standard idioms
for use in macros, to deal with this problem, by tracking the arities
of various tuples, and doing the right thing -- which also supposes
some kind of static typing enforced by the programmer at the macro-level.
In any case, not the kind of thing that goes well with the Lisp tradition.

At the very least, the proponents of such a proposal would have to
demonstrate the idioms for writing correct macros
and dynamically introspecting code in presence of this magic identity
(tuple x) == x
I'd be very curious about such idioms.
Maybe they already exist in CRML or in camlp4?

Oh, and as for why macros are useful... I posted a reply to
your earlier challenge on comp.lang.functional
Message-ID: <87lmcwl...@Samaris.tunes.org>
but it went unanswered, whether it made its point or not.

[ François-René ÐVB Rideau | Reflection&Cybernethics | http://fare.tunes.org ]
[ TUNES project for a Free Reflective Computing System | http://tunes.org ]
Merely having an open mind is nothing; the object of opening the mind,
as of opening the mouth, is to shut it again on something solid.
-- G.K. Chesterton

Sander Vesik

未読、
2002/04/11 9:12:182002/04/11
To:
Eli Barzilay <e...@barzilay.org> wrote:
> Matthias Blume <matt...@shimizu-blume.com> writes:
>
>> 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.
>
> What about (+ (f) (f)) where f returns two values?
>

I very much hope you get either a run or compile time error.

--
Sander

+++ Out of cheese error +++

Barry Margolin

未読、
2002/04/11 10:59:112002/04/11
To:
In article <fok7rfx...@blume-pcmh.research.bell-labs.com>,

Matthias Blume <matt...@shimizu-blume.com> wrote:
>Barry Margolin <bar...@genuity.net> writes:
>> The point is that first class objects can be referenced in locations and
>> they retain their identity. If a tuple is stored in a tuple and then loses
>> its tuple-ness, it's not first class.
>
>This is nonsense. "First-class" means that things are values that can be
>passed to functions, returned to functions, and be stored in datastructures.
>The notion of "identity" does not come in at all. There are programming
>languages where there is no notion of "identity" in the Scheme or Lisp sense,
>and yet, there are first-class values.

But if the tuple ceases to exist in some cases, it seems like a second
class type to me.

>The expression (tuple <arg>) does not allocate anything but simply returns
>its argument, by definition. I do not see how such a definition in any
>way compromises first-class-ness.

If you can have nested tuples, something feels wrong about this, since you
lose a level of nesting any time you have a one-element tuple.

I would expect that (tuple? (tuple ...)) should always return #t. But if a
one-element tuple is equivalent to its element, then (tuple? (tuple x)) is
equivalent to (tuple? x).

Or are you suggesting that whenever a function that doesn't "care" about
tuples receives a one-element tuple, it automatically decomposes it to its
element? E.g. 'tuple?' would still return #t in the above expression, but
you could also do (+ (tuple 3) 4) and it would be treated like (+ 3 4)?
This automatic decomposition seems un-Scheme-like to me, especially since
it only applies for singleton tuples; however, there's some precedent in
the fact that promises are also allowed to force themselves automatically
when necessary (but this isn't required, it's just a possible extension
mentioned in RnRS).

>Tuples are never "spread". The only "exception" of sorts is that
>one-component tuples are identical to their single component. That's
>how things work in math, too.

A tuple is an ordered set, right? In math, there's a difference between a
set containing one element and the element itself (you can even have a set
whose only element is the null set -- this set is not considered null). I
don't know offhand if tuples are treated differently by mathematicians, but
since they're usually pretty rigorous about stuff like this I doubt it.

ozan s. yigit

未読、
2002/04/11 11:03:212002/04/11
To:
ds...@goldshoe.gte.com (Dorai Sitaram):

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

not really. for several reasons: i have not done anything with scheme values
call-with-values for years, and whatever was done was in a commercial setting,
where posting sources to win points in a usenet argument is not really
appreciated.

the other reason i won't bother with samples is that my point, small though
it is, should be reasonable to most people who has to produce code that is
understandable, maintainable and reliable. [of course, matthias is in ML
up to his eyelashes, so this must resonate strongly... :-]

matthias has raised his points before; MLers are happy with their clean tuple
system to express multiple results and arguments, which is why he is arguing
against scheme's values. [pun intended]

this reminds me: programmers take advantage of an ML-like uniformity in perl
to express multiple results; so it is rare to see perl functions that return
multiple values in hashes, concatanated strings or anything else. so for
a million or more programmers, it is simply idiomatic to write

($a, $b, $c) = func();

($dev, $ino, ...) = stat($filename);

($p1, $p2) = split(/:/, $line);

while (($key, $value) = each (%tab)) {
...
}


[i don't know how this will change in perl6, if it does at all]

oz
---
seek not to follow in the footsteps of men of old; seek what they sought.
- matsuo basho

ozan s. yigit

未読、
2002/04/11 11:11:282002/04/11
To:
step...@wsan03.win.tue.nl (Stephan H.M.J. Houben):

> 2. It prevents "leakage". "Leakage" is when implementation details of
> a procedure are visible to the caller. In the case of returing
> a list, the fact that the list is returned is leakage, and more seriously,
> the indentity of the list and what happens when the list gets mutated
> is leakage.

good point. if memory serves, this point is discussed at length in an old
paper by friedman and lawall "towards leakage containment." there was no
followup to the issues raised in that paper, so far as i know.

oz
---
good, fast, cheap. pick two. -- unknown

Barry Margolin

未読、
2002/04/11 11:11:492002/04/11
To:
In article <3CB52F2C...@quiotix.com>,

Right. The point of using multiple values for optimization purposes is
that you expect that you can get some *extra* benefit over normal list
optimizations when you know that the elements are going to be used in a
specific way.

If you already have sufficient optimization in your list constructions and
application, then MV's may not allow you to optimize further. But in
general, if you provide more information about how data will be used, it
gives the implementation more opportunity to optimize it better.

The reason why MV is controversial in the Scheme community is probably
related to that. Traditionally, Scheme has been a very simple language.
Much of its original motivation was that you can have a language with a
minimal set of control and data flow operations, and construct a powerful
language from that foundation. By keeping the language simple, the program
analysis is less complicated, and potential optimizations should be
recognized easily. Since MV seems like something that can be emulated
easily with existing features, it seems like an unnecessary addition.

Debate in the Scheme community is frequently about where to draw the line.
The above philosophy is obviously not taken to its logical extreme, or you
would be programming with the Y-operator all the time.

Thant Tessman

未読、
2002/04/11 11:41:212002/04/11
To:
Matthias Blume wrote:

[...]

>>ISTM that either tuples as top-level elements of tuples should always
>>be spread, or they shouldn't.
>>
>
> Tuples are never "spread". The only "exception" of sorts is that
> one-component tuples are identical to their single component. That's
> how things work in math, too.

As impressed as I am with the rest of your proposal, this part bothers
me. Tuples are implemented in SML in such a way as to encourage us to
think of them as a kind of record where the fields are implicitly
labeled according to their position. With this mental model, I don't see
the justification for making the exception that a record with a single
item with a specific label is equivalent to the item. The exception
seems to be justified for the entirely mundane purpose of getting the
programming language notation to match our preconceived notions of
syntactic convention.

I'm willing to accept the notion that one-component tuples are identical
to their single component, but it sure does seem to follow that two
two-component tuples are identical to a four-component tuple. It's only
our temptation to assign them field names based on their position that
gets in the way of this idea.

-thant

Richard Kelsey

未読、
2002/04/11 11:54:052002/04/11
To:
step...@wsan03.win.tue.nl (Stephan H.M.J. Houben) wrote in message news:<a93ioi$lr4$1...@news.tue.nl>...

>In article <folmbva...@blume-pcmh.research.bell-labs.com>,
>Matthias Blume wrote:
>>Well, that just makes the point very clearly: multiple values are a
>>*performance* hack.
>
> Well, there are some other reasons for having them:
>
> 1. It makes it possible to create continuations that accept an arbitrary
> number of arguments, thereby removing a distinction between
> continuations and "ordinary" procedures in this regard.
>
> 2. It prevents "leakage". "Leakage" is when implementation details of
> a procedure are visible to the caller. In the case of returing
> a list, the fact that the list is returned is leakage, and more seriously,
> the indentity of the list and what happens when the list gets mutated
> is leakage.

3. It means that instead of having N slightly different interfaces
for the same thing you have only one, making it much easier to
read and run other peoples' code.

4. It allows (but, being RnRS, doesn't require) implementations to
provide much better error detection and reporting than is possible
with non-native implementations.

My guess is that reason 3, combined with being easy to implement (if
you don't care too much about performance or error detection) is the
main reason that multiple-value returns are in R5RS. After all,
reason 3 is why R3RS was produced in the first place.

Reason 4 is why I use them, especially '(values)', which can be very
helpful in detecting attempts to use meaningless, 'unspecified' values.

-Richard Kelsey

Matthias Blume

未読、
2002/04/11 11:59:542002/04/11
To:
Sander Vesik <san...@haldjas.folklore.ee> writes:

That's what you get.

-Matthias

Matthias Blume

未読、
2002/04/11 12:31:532002/04/11
To:
Barry Margolin <bar...@genuity.net> writes:

> In article <fok7rfx...@blume-pcmh.research.bell-labs.com>,
> Matthias Blume <matt...@shimizu-blume.com> wrote:
> >Barry Margolin <bar...@genuity.net> writes:
> >> The point is that first class objects can be referenced in locations and
> >> they retain their identity. If a tuple is stored in a tuple and then loses
> >> its tuple-ness, it's not first class.
> >
> >This is nonsense. "First-class" means that things are values that can be
> >passed to functions, returned to functions, and be stored in datastructures.
> >The notion of "identity" does not come in at all. There are programming
> >languages where there is no notion of "identity" in the Scheme or Lisp sense,
> >and yet, there are first-class values.
>
> But if the tuple ceases to exist in some cases, it seems like a second
> class type to me.

It does not "cease to exist". A 1-tuple *is* the value itself. Don't
think of tuples as some sort of "containers" that exist independently
of their contents. There are no mutation operations for tuples,
tuples do not (or, I should rather say, should not) have an identity
that is independent of the identity of its constituents.

Again, this is just like with numbers: Numbers are first-class values,
and (+ 1 x) produces a number that is different from x, while (+ 0 x)
produces a number that *is* x. (In Scheme and Lisp there are some
quirky things going on wrt. eq?, but that's another story. Numbers, again,
are a good example for why we should not care since (eq? x (+ x 0)) may
or may not be #t in different implementations.)

> >Tuples are never "spread". The only "exception" of sorts is that
> >one-component tuples are identical to their single component. That's
> >how things work in math, too.
>
> A tuple is an ordered set, right?

Wrong. A tuple is an element taken from the cartesian product of its
element domains. The product of a single domains is commonly
identified with that domain.

> In math, there's a difference between a
> set containing one element and the element itself (you can even have a set
> whose only element is the null set -- this set is not considered null). I
> don't know offhand if tuples are treated differently by mathematicians, but
> since they're usually pretty rigorous about stuff like this I doubt it.

There is actually nothing inherently wrong with making (tuple <x>)
different from <x>. The only problem is that we then also have to
start distinguishing between two kinds of one-argument functions: All
functions take one argument, but some of them expect that one argument
to be a 1-tuple. This leads to the need for additional syntax: We
need to be able to express "generic" functions for which we don't care
what their argument is, and we need syntax to call such a function
given we already have the argument for it. This is in contrast to
calling a 1-tuple function where the calling syntax implicitly
wraps the given argument with its 1-tuple.

--
-Matthias

Matthias Blume

未読、
2002/04/11 13:35:502002/04/11
To:
Thant Tessman <th...@acm.org> writes:

> Matthias Blume wrote:
>
> [...]
>
> >>ISTM that either tuples as top-level elements of tuples should always
> >>be spread, or they shouldn't.
> >>
> >
> > Tuples are never "spread". The only "exception" of sorts is that
> > one-component tuples are identical to their single component. That's
> > how things work in math, too.
>
> As impressed as I am with the rest of your proposal, this part bothers
> me. Tuples are implemented in SML in such a way as to encourage us to
> think of them as a kind of record where the fields are implicitly
> labeled according to their position.

The truth is both stronger and weaker: In SML, tuples with more than 1
component (and those are the only tuples that exist) have a type that
is *equal* to a corresponding record type with numeric labels. E.g.,

t1 * t2 * t3 = { 1 : t1, 2 : t2, 3 : t3 }

However, there is no 1-tuple type, and it does *not* hold that

t = { 1 : t }

The latter type ({ 1 : t }), which is a 1-element record whose label
happens to be 1, is different from t.

As I have said in a reply to Barry's article, it is possible to go
the route and make 1-tuples be similar to ML's 1-element records.
But there are some drawbacks such as having to distinguish between
functions taking 1 argument, functions taking 1 argument that is a 1-tuple,
functions taking 1 argument that is a 1-tuple whose only component is
a 1-tuple, functions taking 1 argument that is a 1-tuple whose only
component is a 1-tuple whose only component ...

Basically, if you have (tuple x) != x, then when calling a function
that takes 1 argument (as all do in my proposal) and you already have
that argument in some variable, you still need to think about how
many layers of tuple-wrapping need to be added or removed to satisfy
what the function expects. I would find that annoying.

-Matthias

Al Petrofsky

未読、
2002/04/11 13:56:442002/04/11
To:
kel...@s48.org (Richard Kelsey) writes:
>
> 4. It allows (but, being RnRS, doesn't require) implementations to
> provide much better error detection and reporting than is possible
> with non-native implementations.

> Reason 4 is why I use them, especially '(values)', which can be very


> helpful in detecting attempts to use meaningless, 'unspecified' values.

It sounds like you are relying on (let ((x (values))) 1) signalling an
error and (let () (values) 1) being okay. Unfortunately, r5rs defines
both as errors. This strictness makes the expressive power of r5rs
multiple values pretty weak.

I long wondered why r5rs neglected to allow (let () (values) 1). Last
year, I took a look through the archives of the rnrs authors mailing
list. It appears the 1989 vote on whether to allow this in r4rs was
14 to 1 in favor. By the rnrs rules of consensus, the lone dissenter
prevailed. Apparently, that person didn't want call-with-values at
all, and thus it was kept out of r4rs. Three years later at the 1992
meeting, the next and last formal meeting, an old version of
call-with-values got dusted off and unanimously approved. In 1993 it
was realized that the version that was approved did not allow multiple
values in command continuations, but by then it was too late. Thus we
got stuck with the current semantics even though there doesn't appear
to have ever been anybody who simultaneously believed that
call-with-values should be added, but that (let () (values) 1) should
not be allowed.

Oh well.


-al

Matthias Blume

未読、
2002/04/11 13:49:042002/04/11
To:
Francois-Rene Rideau <fa...@tunes.org> writes:

> Let me try to restate the Matthias Blume proposal for having
> tuples behave in some Scheme dialect like they do in ML.
>
> One way to explain the idea to Lispers is to tell them that
> instead of a one generic n-ary tuple constructor, we instead have
> a family of fixed-arity operators tuple/0, tuple/1, tuple/2, ... tuple/n
> and a generic function tuple that dispatches over these operators.
> NOW, in the ML case, that Matthias recommends for a Lisp dialect,
> the case n=1 is special-cased to identity,
> and optionally tuple/0 is special-cased to nil '(), #f or a special unit,
> while for n>=2 and for n>=2 only is tuple/n some kind of constructor
> much like vector (could be vector itself, except that in ML, it's read-only).
>
> This means that tuple is not a constructor for n=1, so that tuple/1 doesn't
> return something recognizable by a predicate tuple?, unless its unique
> argument is a tuple for n>=2. This can be very confusing for generic programs
> using dynamic type introspection, and goes counter to the Lisp tradition.

Fine. I do not really care about Lisp tradition. The point about
generic programs is well taken, though. (I still don't think that it
is really a problem.) Even if the built-in TUPLE is not the identity
in the single argument case, it is easy enough for programmers to
define one that is. In general, generic programming tools need to
take such things into account.

> The problem becomes particularly acute because of subtle interactions
> induced by this special case for dynamically generated code.
> For instance, if a macro-generated program expands to
> (tuple-nth n x)
> this might suddenly have bizarre effects due to x being a 1-tuple containing
> a 2-tuple: and instead of returning the first member of the generic n-tuple
> where n happens to be 1 in the generated code, such a generated program
> would access the first member of the contained 2-tuple. BAD.

This can be prevented, e.g., by adding a dummy component so that the
1-tuple case does not arise. Been there, done that.

> ML doesn't have to cope with macro-generated code, and avoids this issue
> at the programmer-level by resorting to static type-checking.

The same thing happens in ML when you have program-generated code.
(Macros are not the only example.) I have personally run into this.
Yes, it is a problem, but I don't think it is a fatal one.

> Oh, and as for why macros are useful... I posted a reply to
> your earlier challenge on comp.lang.functional
> Message-ID: <87lmcwl...@Samaris.tunes.org>
> but it went unanswered, whether it made its point or not.

Someone else posted an answer that made the point much more
succinctly. I personally still don't like macros (anymore). But
that's really another topic.

--
-Matthias

Eli Barzilay

未読、
2002/04/11 18:41:212002/04/11
To:
Matthias Blume <matt...@shimizu-blume.com> writes:

OK, so -- if I get you right, then given:

(define (f x) (if x (values 1) (values 1 2)))

you'll get:

(+ (f #f)) -> 3
(+ (f #t) (f #t)) -> 2
(+ (f #t) (f #f)) -> run time error

If this is the case, then (a) it seems a bit messy, and (b) I don't
see why this could not have been done on top of the current state of
things.

To minimize (a) I think it could have been better to make value tuples
something that gets spliced into a function call point, so the last
expression would return 4. But even in this case, I still don't see
how your think is anything different than just expanding on the
current values thing.

Matthias Blume

未読、
2002/04/11 22:23:072002/04/11
To:
Eli Barzilay <e...@barzilay.org> writes:

> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Sander Vesik <san...@haldjas.folklore.ee> writes:
> >
> > > Eli Barzilay <e...@barzilay.org> wrote:
> > > > Matthias Blume <matt...@shimizu-blume.com> writes:
> > > >
> > > >> 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.
> > > >
> > > > What about (+ (f) (f)) where f returns two values?
> > > >
> > >
> > > I very much hope you get either a run or compile time error.
> >
> > That's what you get.
>
> OK, so -- if I get you right, then given:
>
> (define (f x) (if x (values 1) (values 1 2)))

I find this sort of thing (varying the number of return values at
runtime) messy, so I don't really care what the ramifications are. To
me, functions should return k values for some fixed k.

But let's assume that we have

(define (g x) 1)
(define (h x) (values 1 2))

Then...

>
> you'll get:
>
> (+ (f #f)) -> 3

(+ (h x)) -> 3

> (+ (f #t) (f #t)) -> 2

(+ (g x) (g x)) -> 2

> (+ (f #t) (f #f)) -> run time error

(+ (g x) (h x)) -> runtime error, indeed (type error, one argument to + is not a number)

All this is just like, say, in SML, where assuming we have

fun g () = 1
fun h () = (1, 2)

we get

op+(h()) --> 3
op+(g(),g()) aka g()+g() --> 2
op+(g(),h()) aka g()+h() --> (compile-time) type error

> If this is the case, then (a) it seems a bit messy,

Why?

> and (b) I don't
> see why this could not have been done on top of the current state of
> things.

Getting the above behavior is not the point. The point is that I want
to be able to write higher-order functions without having to worry
what the arity (# of arguments and/or results) might be. One more
time, I want to be able to write

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

and

(map f (map g l))

regardless of how many arguments/results f or g might have.

> To minimize (a) I think it could have been better to make value tuples
> something that gets spliced into a function call point, so the last
> expression would return 4. But even in this case, I still don't see
> how your think is anything different than just expanding on the
> current values thing.

The difference is in the fact that under my proposal, multiple values are
*first class*. They currently are not (although one can clumsily turn
MVs into lists and vice versa).

The rest was more or less idle speculation, the point being that such
a thing does not necessarily have to be any less efficient (as you had
suggested) than what we currently have.

I don't think that anyone is actually going to change Scheme the way I
suggested. I also don't really care because the language that I use
for my daily work already does all of the things that I described (and
then some).

--
-Matthias

Eli Barzilay

未読、
2002/04/12 1:53:542002/04/12
To:
Matthias Blume <matt...@shimizu-blume.com> writes:

> Eli Barzilay <e...@barzilay.org> writes:
>
> > OK, so -- if I get you right, then given:
> >
> > (define (f x) (if x (values 1) (values 1 2)))
>
> I find this sort of thing (varying the number of return values at
> runtime) messy, so I don't really care what the ramifications are.
> To me, functions should return k values for some fixed k.

I agree that it is messy, but forbidding it (or leaving such cases as
unspecified) gets you one step closer to a strict world, which is not
a issue if you're used to static languages... Anyway, you showed that
you still get the same problem.


> > (+ (f #t) (f #f)) -> run time error
>
> (+ (g x) (h x)) -> runtime error, indeed (type error, one
> argument to + is not a number)
>

> All this is just like, say, in SML, [...]

Yeah OK, so it's pretty clear now that you want exactly the same
product types as in ML -- so (tuple x y) would be a pair, (tuple) is
some unit value and (tuple x) is the same as x. And we
(unsurprisingly) get to the same problem that several other branches
of this thread have gotten to. (Continued below.)


> > If this is the case, then (a) it seems a bit messy,
>
> Why?

Because you want (f x y) to be a syntactic sugar for (f (values x y))
but (f x) be just that. This mess doesn't exists if you think about
ML tuples since (f x) would be (f (values x)) but (values x) is just
x. (Again, more about messiness below.)


> The difference is in the fact that under my proposal, multiple
> values are *first class*. They currently are not (although one can
> clumsily turn MVs into lists and vice versa).

I think that this is the main communication problem here -- on the
Scheme side, when you say that you want multiple values as first class
objects it sounds like you should have a new constructor. But you
don't want exactly that since, for example, (values? x) -> #t for
every x. I think that the existing `values' is similar to what you
want -- except that it's not return a first class object. The main
thing that you seem to care for is not handling multiple values
explicitly, which could be done manipulating function invocations,
which is actually possible in MzScheme's new syntax thing by playing
with the `#%app' special synax -- define this:

(module values mzscheme
(define-syntax application
(syntax-rules ()
((_ x y) (#%app call-with-values (lambda () y) x))
((_ x y ...) (#%app x y ...))))
(provide (rename application #%app)))

and you get:

> (require values)
> (+)
0
> (+ 1)
1
> (+ 1 2)
3
> (+ (values))
0
> (+ (values 1))
1
> (+ (values 1 2))
3
> (+ 1 (values 2 3))
context expected 1 value, received 2 values: 2 3
> (define (f) (values 1 2 3))
> (+ (f))
6
> (define (f x) (values (+ x 1) (+ x 2)))
> (define (compose f g) (lambda x (f (apply g x))))
> ((compose + f) 2)
7

You could even define simple `values' functions:

(define (values-count . xs) (length xs))

but this is only for a single argument, otherwise you have to resort
to things like:

(define (values-project n) (lambda xs (list-ref xs n)))

I think that overcoming this means an actual tuple implementation,
with things similar to what you were talking about, except that I'd do
everything at the point of calling a function:

(module values mzscheme
(define-struct tuple (xs))
(define (tuple-values . xs) ; will override primitive `values'
(if (and (not (null? xs)) (null? (cdr xs)))
(car xs)
(make-tuple xs)))
(define-syntax application
(syntax-rules ()
((_ x y) (let ((v y))
(if (tuple? v)
(#%app apply x (#%app tuple-xs v))
(#%app x v))))
((_ x y ...) (#%app x y ...))))
(provide (struct tuple (xs))
(rename tuple-values values)
(rename application #%app)))

and this gets the opposite problem -- a function that just gets a
single tuple object is impossible:

> (require values)
> (values 1 2)
#<struct:tuple>
> (tuple-xs (values 1 2))
tuple-xs: expects 1 argument, given 2: 1 2

My opinion was that this problem is inherent in the desire to avoid a
redundant values syntax -- solving it in a clean way is going down
that ML-syntax path. (It would be nice if there was a good solution.)


> The rest was more or less idle speculation, the point being that
> such a thing does not necessarily have to be any less efficient (as
> you had suggested) than what we currently have.

Ah ok, I thought you wanted it to be more.


> I don't think that anyone is actually going to change Scheme the way
> I suggested. I also don't really care because the language that I
> use for my daily work already does all of the things that I
> described (and then some).

Heh, and these two sentences bring back my initial point in this
thread...

Stephan H.M.J. Houben

未読、
2002/04/12 3:16:562002/04/12
To:
In article <VIht8.9$EE6...@paloalto-snr2.gtei.net>, Barry Margolin wrote:
>In article <3CB52F2C...@quiotix.com>,
>Jeffrey Siegal <j...@quiotix.com> wrote:
>>Barry Margolin wrote:
>>> When you're compiling the function that returns the list, you don't know
>>> that the callee is planning on using it as the argument to apply.
>>
>>It isn't clear that you necessarily need to know that.
>>
>>> Compile-time optimization is only possible if you do block compilation of
>>> the callee and all its callers.
>>
>>"Compile-time optimization" is a vague term that could mean any number
>>of things, including making list (or vector--see below) operations
>>themselves very efficient. so there isn't any (or much) need to avoid
>>them. As Marc Feeley pointed out, the major factors making multiple
>>values in Gambit efficient is inlining the allocation and using a vector
>>rather than a list (presumably this reduces the number of allocation
>>operations and the size and time cost of dealing with CDR cells).
>
>Right. The point of using multiple values for optimization purposes is
>that you expect that you can get some *extra* benefit over normal list
>optimizations when you know that the elements are going to be used in a
>specific way.

In fact, this expectation is warranted. Note that there can only be
one "multiple values box" be active at any point in time, so that the
implementation can re-use the same multiple values box again and again.

I.e. an implementation can create a non-consing multiple values return
so that only values and call-with-values know about multiple values.
The rest of the implementation machinery doesn't need to be aware
of multiple values. Also, no new implementation types are needed.

This could even be done in R5RS Scheme without values, were it not for the
fact that values is a vararg procedure and therefore has to cons its
arguments into a list, if it is to be implemented in R5RS Scheme.

Stephan

Al Petrofsky

未読、
2002/04/12 5:48:032002/04/12
To:
"felix" <felixu...@freenet.de> writes:

> Jeffrey Siegal wrote in message <3CB363BC...@quiotix.com>...
> >

> > It might make optmization somewhat easier, but even that seems
> >questionable.
>
> 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).

I just reread the Ashley/Dybvig paper, and it seems that their scheme
could just as easily be applied to apply and list, rather than
to call-with-values and values.

Their approach is for the compiler to put a multivalued return handler
at a fixed offset from every return point. When values is called, it
adds this offset to the continuation address it is given, and jumps
there. For most continuations, which only accept one value, this
location will contain a jump to an error signaller. Code wanting to
return a single value simply jumps to the regular return point, and
thus there is no testing or other speed penalty for code that eschews
call-with-values.

Why couldn't you do the same thing with apply and list? At a fixed
offset from every return point would be a "list values" return
handler. When the list procedure is invoked, instead of calling the
list allocator, it jumps to this offset of its continuation. For most
continuations, this return point will then call the real list
allocater and carry on. For apply continuations, it will simply leave
the arguments in their registers/stack locations, and call the
procedure being applied.


Hmm. Perhaps you can't avoid having this cost one extra jump (or
alternatively, one extra test) for each call to list that occurs in a
procedure's tail position. I think it still might be a big win for
code that uses apply/lambda/list for binding to multiple results. All
those calls to list whose continuation is obviously not the second
argument of an apply could still be compiled into direct calls to the
real list allocator. (I'm assuming, as did Ashley and Dybvig, that
we're not doing extensive whole-program analysis, but that we are able
to know that the standard procedures will not be redefined.)


-al

Matthias Blume

未読、
2002/04/12 11:12:082002/04/12
To:
Eli Barzilay <e...@barzilay.org> writes:

> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Eli Barzilay <e...@barzilay.org> writes:
> >
> > > OK, so -- if I get you right, then given:
> > >
> > > (define (f x) (if x (values 1) (values 1 2)))
> >
> > I find this sort of thing (varying the number of return values at
> > runtime) messy, so I don't really care what the ramifications are.
> > To me, functions should return k values for some fixed k.
>
> I agree that it is messy, but forbidding it (or leaving such cases as
> unspecified) gets you one step closer to a strict world, which is not
> a issue if you're used to static languages... Anyway, you showed that
> you still get the same problem.

Right, without a type system to rule this out from the start, one has
to take the possibility into account.

>
> > > (+ (f #t) (f #f)) -> run time error
> >
> > (+ (g x) (h x)) -> runtime error, indeed (type error, one
> > argument to + is not a number)
> >
> > All this is just like, say, in SML, [...]
>
> Yeah OK, so it's pretty clear now that you want exactly the same
> product types as in ML -- so (tuple x y) would be a pair, (tuple) is
> some unit value and (tuple x) is the same as x. And we
> (unsurprisingly) get to the same problem that several other branches
> of this thread have gotten to. (Continued below.)
>
>
> > > If this is the case, then (a) it seems a bit messy,
> >
> > Why?
>
> Because you want (f x y) to be a syntactic sugar for (f (values x y))
> but (f x) be just that. This mess doesn't exists if you think about
> ML tuples since (f x) would be (f (values x)) but (values x) is just
> x. (Again, more about messiness below.)

I still don't see how this is messier or any different from the ML situation.
In ML, we also have:

f x === f (x)

and, more generally, x === (x). The only difference in SML is that
I don't have to explicitly mention the word "tuple" or "values" because
this is built into the syntax of parenthesized, comma-separated expressions.


> > The difference is in the fact that under my proposal, multiple
> > values are *first class*. They currently are not (although one can
> > clumsily turn MVs into lists and vice versa).
>
> I think that this is the main communication problem here -- on the
> Scheme side, when you say that you want multiple values as first class
> objects it sounds like you should have a new constructor.

I don't know why it sounds that way to you, it surely doesn't to me.
*This* seems to be our main miscommunication. Again, numbers are
first-class values, and yet, you cannot distinguish between x and the
result of doing (+ x).

By the way, I would be perfectly happy to disallow applying tuple to
precisely one argument. Nothing is lost that way in my proposal.
Basically, tuples are for communicating between a producer and a consumer
that have an implicit understanding of how many values they are going
to transmit/receive. With this, there will never be a confusion about
the tuple-of-one-value thing: If I expect one value, then the one value
I got is it. No need to unwrap any superfluous constructors.

Admittedly, this mechanism does not work for functions such as "list" because
when you say

(list x)

and x happens to hold a tuple of, say, 2 values, then we don't know
whether to construct a single-element list whose element is a 2-tuple
or a 2-element.

[Side note: To me, this nicely ties in with another pet peeve of mine:
Variable-arity functions. Frankly, I don't need them and I don't want
them. In cases where I truly want to pass a variable number of things
to a function, I can do so explicitly by passing a list. The number
of times I used + or * with anything other than precisely 2 arguments
I can probably count on the fingers of one hand. If the need arises,
I am perfectly willing to "fold" explicitly, e.g., instead of
saying

(apply + l)

I say

(foldl + 0 l)

or such. In cases where we truly don't want to give up the
notational convenience of variable arity function calls (e.g., LIST),
we can recover the loss using a simple macro.]

> But you
> don't want exactly that since, for example, (values? x) -> #t for
> every x.

Well, in my proposal I don't actually need to expose a values?
predicate to the user (although not doing so might be considered even
more against the spirit of Scheme). Basically, a general
interpretation of your values? predicate would be: "Does the argument
denote any number (0 or more) values?". Obviously, the answer to that
*has* to be #t, so having such a predicate is pretty useless.

The other alternative is to have a predicate that inspects the
*representation* of a value. Such a predicate would return #t for
tuples with more than 1 component and false otherwise. But the
question it is asking is different, namely: "Does the argument
denote a tuple of more than 1 value?"

> I think that the existing `values' is similar to what you
> want -- except that it's not return a first class object.

But "first class" *is* what I want! Dammit! :-)

--
-Matthias

Dorai Sitaram

未読、
2002/04/12 11:41:592002/04/12
To:
In article <foads95...@trex8.cs.bell-labs.com>,

Matthias Blume <matt...@shimizu-blume.com> wrote:
>[Side note: To me, this nicely ties in with another pet peeve of mine:
>Variable-arity functions. Frankly, I don't need them and I don't want
>them. In cases where I truly want to pass a variable number of things
>to a function, I can do so explicitly by passing a list. The number
>of times I used + or * with anything other than precisely 2 arguments
>I can probably count on the fingers of one hand. If the need arises,
>I am perfectly willing to "fold" explicitly, e.g., instead of
>saying
>
> (apply + l)
>
>I say
>
> (foldl + 0 l)
>
>or such. In cases where we truly don't want to give up the
>notational convenience of variable arity function calls (e.g., LIST),
>we can recover the loss using a simple macro.]

But I thought you hated macros too, no?

Matthias Blume

未読、
2002/04/12 13:30:112002/04/12
To:
ds...@goldshoe.gte.com (Dorai Sitaram) writes:

Built-in syntax for LIST would be fine by me. That's really all one would
need.

-Matthias

Joe Marshall

未読、
2002/04/12 14:39:582002/04/12
To:

"Matthias Blume" <matt...@shimizu-blume.com> wrote in message
news:foads95...@trex8.cs.bell-labs.com...

> [Side note: To me, this nicely ties in with another pet peeve of mine:
> Variable-arity functions. Frankly, I don't need them and I don't want
> them. In cases where I truly want to pass a variable number of things
> to a function, I can do so explicitly by passing a list. The number
> of times I used + or * with anything other than precisely 2 arguments
> I can probably count on the fingers of one hand. If the need arises,
> I am perfectly willing to "fold" explicitly, e.g., instead of
> saying
>
> (apply + l)
>
> I say
>
> (foldl + 0 l)
>
> or such. In cases where we truly don't want to give up the
> notational convenience of variable arity function calls (e.g., LIST),
> we can recover the loss using a simple macro.]

It would have to be a variable arity macro, of course,
and as such, could not be implemented in within the
language. I suppose you could use CPP.....

Matthias Blume

未読、
2002/04/12 14:58:052002/04/12
To:
"Joe Marshall" <prunes...@attbi.com> writes:

Huh?

(define-syntax list
(syntax-rules ()
((_) '())
((_ x y ...) (cons x (list y ...)))))

--
-Matthias

Eli Barzilay

未読、
2002/04/12 15:40:022002/04/12
To:
Matthias Blume <matt...@shimizu-blume.com> writes:

> I still don't see how this is messier or any different from the ML
> situation. In ML, we also have:
>
> f x === f (x)
>
> and, more generally, x === (x). The only difference in SML is that
> I don't have to explicitly mention the word "tuple" or "values"
> because this is built into the syntax of parenthesized,
> comma-separated expressions.

One thing I said at the end of my post was exactly this -- that the
messiness comes from trying to abuse syntax too much.


> > I think that this is the main communication problem here -- on the
> > Scheme side, when you say that you want multiple values as first
> > class objects it sounds like you should have a new constructor.
>
> I don't know why it sounds that way to you, it surely doesn't to me.

I wasn't talking specifically about myself, just pointing at a likely
communication problem you have in this thread generally -- I think
that at least three different people wanted to see a `tuple?'
predicate, which is obviously something that don't popup if you think
about ML products.


> *This* seems to be our main miscommunication. Again, numbers are
> first-class values, and yet, you cannot distinguish between x and the
> result of doing (+ x).

I know, and both my toy implementations did exactly that -- sounds
like you're confusing me with someone else.


> [...] No need to unwrap any superfluous constructors. [...]

Did you even look at my code? (Or what it looks like to use it?)


> and x happens to hold a tuple of, say, 2 values, then we don't know
> whether to construct a single-element list whose element is a 2-tuple
> or a 2-element.

=> mess.


> [Side note: [...] Variable-arity functions. Frankly, I don't need

Side ML-rant: printf.

> them and I don't want them. [...]]


> [...] Basically, a general interpretation of your values? predicate


> would be: "Does the argument denote any number (0 or more)
> values?". Obviously, the answer to that *has* to be #t, so having
> such a predicate is pretty useless.

*sigh*

This is, again, exactly what I said trying to point out the
miscommunication you're experiencing. The fact that exposing tuples
as first order values in Scheme ('s spirit) you should have such a
predicate, but the fact that it is useless means that this is
something different than other standard first-order values (at least
the constructor is different). The fact that you so carefully explain
the exact thing that I wrote indicates that I'm throwing words to
empty spaces.


> The other alternative is to have a predicate that inspects the
> *representation* of a value. Such a predicate would return #t for
> tuples with more than 1 component and false otherwise. But the
> question it is asking is different, namely: "Does the argument
> denote a tuple of more than 1 value?"

*double-sigh*cough*cough*

Yeah, second implementation I posted above.


> > I think that the existing `values' is similar to what you
> > want -- except that it's not return a first class object.
>
> But "first class" *is* what I want! Dammit! :-)

Well, if you got pissed enough at this point, then the source of
redundancy in this exchange is clarified. I showed how it is possible
to change function invocations so the simple compose function will
work properly. Did it have any problem? If it is not enough, then
what example do you have of needing this otherwise?

Erann Gat

未読、
2002/04/12 15:20:332002/04/12
To:
In article <fo1ydk6...@trex8.cs.bell-labs.com>, Matthias Blume
<matt...@shimizu-blume.com> wrote:

But then you couldn't do this cool little parlor trick any more:

(define (transpose m) (apply map list m))

Maybe that's a feature.

E.

Matthias Blume

未読、
2002/04/12 15:58:202002/04/12
To:
Eli Barzilay <e...@barzilay.org> writes:

> > [Side note: [...] Variable-arity functions. Frankly, I don't need
>
> Side ML-rant: printf.

Easy reply: Have a look at
ftp://ftp.research.bell-labs.com/dist/smlnj/contrib/lib/Util.tar.Z,
especially at the files Util/blume/format.*. Or go and read Olivier
Danvy's article on how to make a well-typed printf in ML.

> This is, again, exactly what I said trying to point out the
> miscommunication you're experiencing. The fact that exposing tuples
> as first order values in Scheme ('s spirit) you should have such a
> predicate, but the fact that it is useless means that this is
> something different than other standard first-order values (at least
> the constructor is different). The fact that you so carefully explain
> the exact thing that I wrote indicates that I'm throwing words to
> empty spaces.

That may very well be. We just have different notions of what
constitutes "mess". That's all.

--
-Matthias

Matthias Blume

未読、
2002/04/12 21:14:052002/04/12
To:
Eli Barzilay <e...@barzilay.org> writes:

> Did you even look at my code? (Or what it looks like to use it?)

I admit that originally I didn't, at least not carefully. Now that I have (somewhat
more carefully), I think that it still fails the acid-test for first-class-ness, namely
that if I can write

(f (g x))

then I must also be able to write

(let ((tmp (g x))) (f tmp)) ; where tmp is fresh

and the two things have to be equivalent.

> [ ... ] I showed how it is possible


> to change function invocations so the simple compose function will
> work properly. Did it have any problem? If it is not enough, then
> what example do you have of needing this otherwise?

The compose function is just one example of many. Basically, I want to
be able to do simple simple reasoning, and simple laws such as the
let-expansion above to be valid.

Anyway, I don't think that any further discussion of the (tuple x) =?
x thing is going to bring enlightenment. I proposed this particular
language because I am somewhat familiar with its properties, and I
needed something to make my "the single-argument/single-return model
does not have to be expensive" claim concrete. I believe that something
similar could be done for the (tuple x) != x case. The details are
left as an exercise for the reader.

--
-Matthias

その他のメッセージを読み込んでいます。
新着メール 0 件