In a series of threads back in January 1995, Matthias Blume argued passionately against the presence of values/call-with-values in Scheme on the grounds that they add nothing to the language as a language - that is, they grant no additional expressiveness beyond what is already possible with list and apply, and in fact detract from expressiveness by forcing us to write things like function composition as
(define (compose f g) (lambda args (call-with-values (lambda () (apply g args)) f)))
rather than in the simpler and more obvious way. Matthias felt that the introduction of these constructs was a divergence from Scheme's spirit of orthogonality and challenged Schemers to come up with an argument justifying their inclusion in the language. The sparks flew, and the results appear to have been that most people agreed with Matthias Blume, with the notable exceptions of William Clinger and Kent Dybvig. The primary arguments in favour of values/call-with-values were that they allow implementors to optimise generated code in ways that are impossible or more difficult in the list/apply case.
Matthias (and many others) seemed to feel that this was an extremely suspect reason for altering the foundations of Scheme, and that it amounted to sacrificing the spirit of the language for a "performance hack."
Another justification given for values/call-with-values was that they create a symmetry with the multiple arguments accepted by procedures. Matthias countered by stating that there was no need to have procedures accept multiple arguments at all and that Scheme could learn from languages like ML in this respect.
William Clinger argued in favour of values/call-with-values for some time. But in February 1995, in <3gr4ls$...@narnia.ccs.neu.edu>, he appeared to have second thoughts of a sort. He writes:
> I have misunderstood several of the points that Matthias Blume has > been trying to make. As I now understand his arguments, Matthias has > not been criticizing the multiple values proposal. Instead he has > been criticizing various misfeatures of Scheme that make multiple > values appear necessary. > [...] > I will then outline some changes to Scheme that would make multiple > values unnecessary. These changes are radical, yet they would not > break any code written in R4RS Scheme. > [...] > This proposal would address all three of the above criticisms without > breaking *any* portable Scheme code. On the other hand, this proposal > would require changes to all existing implementations of Scheme.
The proposal involves the introduction of a new data type called a 'sequence'. With this approach,
> [E]very procedure takes exactly one argument and returns exactly one > result. The argument and the result will always be a sequence. [...] > An expression in tail-recursive position returns a one-element > sequence. Continuations accept sequences containing an arbitrary > number of elements as results.
To my surprise, this proposal generated almost no discussion. In fact, it seemed to mark the end of the original one, and since then people seem to have resigned themselves to values/call-with-values, occasional fulminations aside.
My questions:
1. Is this proposal still viable for Scheme, as a language, today?
2. What are the thoughts of implementors on the proposal?
3. Have people really resigned themselves to values/call-with-values, both a) as a specific means of handling multiple return values, and b) as the philosophical departure from the roots of the language they (arguably) represent?
4. Does anyone have any other ideas for dealing with the problem of multiple return values that haven't been raised yet?
> To my surprise, this proposal generated almost no discussion. In fact, > it seemed to mark the end of the original one, and since then people > seem to have resigned themselves to values/call-with-values, occasional > fulminations aside.
> My questions:
> 1. Is this proposal still viable for Scheme, as a language, today?
> 2. What are the thoughts of implementors on the proposal?
> 3. Have people really resigned themselves to values/call-with-values, > both > a) as a specific means of handling multiple return values, and > b) as the philosophical departure from the roots of the language > they (arguably) represent?
> 4. Does anyone have any other ideas for dealing with the problem of > multiple return values that haven't been raised yet?
as of yet I did not bother to implement these. if I did it would probably jusy be in the form of macros: (defmacro values x `(list ,@x))
(defmacro call-with-values (f g) `(apply ,f ,g))
yes, working hygenic macros were another thing I never got to... instead I had a half-working macro system that I just used to implement defmacro...
>>[E]very procedure takes exactly one argument and returns exactly one >>result. The argument and the result will always be a sequence. [...] >>An expression in tail-recursive position returns a one-element >>sequence. Continuations accept sequences containing an arbitrary >>number of elements as results.
> To my surprise, this proposal generated almost no discussion. In fact, > it seemed to mark the end of the original one, and since then people > seem to have resigned themselves to values/call-with-values, occasional > fulminations aside.
> My questions:
> 1. Is this proposal still viable for Scheme, as a language, today?
I certainly believe that values/call-with-values are an area that ought to be looked at for R6RS.
> 2. What are the thoughts of implementors on the proposal?
I prefer it to values/call-with-values but share the concerns expressed by Henry Baker: sequences are second class, and a = [a] is troublesome.
> 3. Have people really resigned themselves to values/call-with-values, > both > a) as a specific means of handling multiple return values, and
No. See answer #1.
> b) as the philosophical departure from the roots of the language > they (arguably) represent?
No, but see answer #2 - if values/call-with-values are a "departure from the roots of the language" then so are sequences (because of their second-class nature).
> 4. Does anyone have any other ideas for dealing with the problem of > multiple return values that haven't been raised yet?
See the "splicing values" idea I posted a while back:
Category 5 wrote: > In a series of threads back in January 1995, Matthias Blume argued > passionately against the presence of values/call-with-values in Scheme > on the grounds that they add nothing to the language as a language - > that is, they grant no additional expressiveness beyond what is already > possible with list and apply, and in fact detract from expressiveness by > forcing us to write things like function composition as
> (define (compose f g) > (lambda args > (call-with-values > (lambda () (apply g args)) > f)))
> rather than in the simpler and more obvious way. Matthias felt that the > introduction of these constructs was a divergence from Scheme's spirit > of orthogonality and challenged Schemers to come up with an argument > justifying their inclusion in the language. The sparks flew, and the > results appear to have been that most people agreed with Matthias Blume, > with the notable exceptions of William Clinger and Kent Dybvig. The > primary arguments in favour of values/call-with-values were that they > allow implementors to optimise generated code in ways that are > impossible or more difficult in the list/apply case. ... > 4. Does anyone have any other ideas for dealing with the problem of > multiple return values that haven't been raised yet?
I'm not quite whether this counts, but it's somewhat related:
* J. Michael Ashley and R. Kent Dybvig. "An Efficient Implementation of Multiple Return Values in Scheme". 1994 ACM Conference on LISP and Functional Programming
> In a series of threads back in January 1995, Matthias Blume argued > passionately against the presence of values/call-with-values in Scheme [...] > My questions:
I'd like to add another question - R5RS states (1) values can be defined as:
Matthias Radestock wrote: > I prefer it to values/call-with-values but share the concerns expressed > by Henry Baker: sequences are second class, and a = [a] is troublesome.
The XQuery language (now being standardized by W3C - see http://www.w3.org/XML/Query) makes sequences first class. It makes for a very useful and interesting language. However, functions still take multiple parameters (each of which can be a sequence), and return a single result (which can be a sequence). It would have been interesting if they'd gone further, and has a single parameter sequence, which you could take apart using pattern matching. I'd like to work out the pragmatics of such a design.
Category 5 <catfive...@chaosnet.org> writes: > William Clinger argued in favour of values/call-with-values for some > time. But in February 1995, in <3gr4ls$...@narnia.ccs.neu.edu>, he > appeared to have second thoughts of a sort. He writes:
> > [...] > > This proposal would address all three of the above criticisms without > > breaking *any* portable Scheme code. On the other hand, this proposal > > would require changes to all existing implementations of Scheme.
> The proposal involves the introduction of a new data type called a > 'sequence'. With this approach, > My questions:
> 1. Is this proposal still viable for Scheme, as a language, today?
Probably not, although, not having looked at the idea *at all*, I find it interesting. I expect that the big implementations (PLT, Bigloo) have too much invested in their current architectures to embrace such a fundamental change.
> 3. Have people really resigned themselves to values/call-with-values, > both > a) as a specific means of handling multiple return values, and > b) as the philosophical departure from the roots of the language > they (arguably) represent?
No, but I've been whingeing about it in the last year or so. As far as your a) goes, I no longer use call-with-values/values in my own code, I pass the multiple-valued continuation directly. This works out better for a lot of reasons, most notably that frequently you use multiple values to return both a condition and a value. In those cases, you simply turn the original function call into (effectively) a conditional construct where you can have *different* sets of values available in different cases. In other multiple-valued scenarios, the syntactic overhead of constructiona lambda expression and passing it down the call chain is, to my tastes, no worse that that involved in using the call-with-values construct in the first place.
I agree completely with Matthias Blume on b)
> 4. Does anyone have any other ideas for dealing with the problem of > multiple return values that haven't been raised yet?
Just drop the construct from the langusge. It's completely unecessary since you can use explicit CPS. I have also posted a different approach to call/values which considers it as a continuation injection operator (as opposed to call/cc, which is a continuation-extraction operator); however, Matthias Blume pointed out to me some type difficulties with that which prevented the implementation of call-with-values as a pure source-code transformation. I believe those could be resolved by changing the semantics of call/values. Of course the only standards body that is likely to ratify *my* suggestions is the Nepalese Journal for Deconstructionist Sociologists and Sheep-herders.
david rush -- He who would make his own liberty secure, must guard even his enemy from repression -- Thomas Paine
David Rush <dr...@aol.net> wrote: > No, but I've been whingeing about it in the last year or so. As far as > your a) goes, I no longer use call-with-values/values in my own code, > I pass the multiple-valued continuation directly.
One point that doesn't get much play in the multiple values debates is the zero values case. When I write a side effecting procedure, I end it with a (values) call. If multiple values were banned, this would be the situation when I would first notice the pinch.
Using cps seems unreasonably heavy handed in this simple situation. An oxymoronic "no-value" value to satisfy "functional" semantics for a non-function seems stilted compared to zero values which seems to exactly capture the situation. How did you handle this case after you jetisoned multiple values from your life?
David Rush <dr...@aol.net> writes: >> 3. Have people really resigned themselves to values/call-with-values, >> both >> a) as a specific means of handling multiple return values, and >> b) as the philosophical departure from the roots of the language >> they (arguably) represent?
> No, but I've been whining about it in the last year or so. As far as > your a) goes, I no longer use call-with-values/values in my own code, > I pass the multiple-valued continuation directly. This works out > better for a lot of reasons, most notably that frequently you use > multiple values to return both a condition and a value. In those > cases, you simply turn the original function call into (effectively) > a conditional construct where you can have *different* sets of values > available in different cases. In other multiple-valued scenarios, the > syntactic overhead of constructiona lambda expression and passing it > down the call chain is, to my tastes, no worse that that involved in > using the call-with-values construct in the first place.
I rarely use values/call-with-values and often go to explicit CPS. One reason is all the damn syntax:
The CPS version is more concise than the call-with-values version!
(Yes, I could write a macro, but then I'd have to ensure it's available, and it won't match other people's macros, etc. etc.)
And while I understand why it is an error to return multiple values to a continuation expecting a single value, it sure is convenient. With the current system, if you wish to add an extra return value, you have to adjust the code at *every* return site (provided you can enumerate all the return sites!). In Common Lisp, unexpected return values are silently discarded, so modifying a function to return extra information is a local change. Discarding the extra return values is somewhat sloppy semantically, but far more convenient from a coding standpoint.
> When an argument of a call is evalutated only one return value is > expected. Thus the evaluation contexts of (values 1 2) and (values 3 > expects only one value but receive two - and an error should be > signaled.
Perhaps I was too fast.
Jens Axel:
>> 4. Does anyone have any other ideas for dealing with the problem of >> multiple return values that haven't been raised yet?
> I'm not quite whether this counts, but it's somewhat related:
> * J. Michael Ashley and R. Kent Dybvig. "An Efficient Implementation of > Multiple Return Values in Scheme". 1994 ACM Conference on LISP and > Functional Programming
There's an interesting footnote in that paper. It says that the text of R5RS does specify what an implementation is to do when an continuation recieves too many values, but that the semantics gives an error. However, it says there's consensus that it's ok to ignore extra values.
Will's version 1 of the wording for R5RS supports this:
3.8. Multiple return values. Not adopted. Debate centered on whether returning a single "multiple value" should be equivalent to returning that value normally. A secondary issue concerned whether extra return values should be ignored or an error. We approached consensus on this.
After much debate it was decided to go on to other issues and let Dybvig and Hanson discuss the matter in private. Their discussion later that night led to a conclusion that we don't understand the issues well enough to charge ahead with either proposal 3.7 or 3.8 or a variation.
> One point that doesn't get much play in the multiple values debates is > the zero values case. When I write a side effecting procedure, I end > it with a (values) call. If multiple values were banned, this would be > the situation when I would first notice the pinch.
This is not necessarily working on R5RS compliant implementations, if the call-site does not expect multiple values.
Anthony Carrico <acarr...@memebeam.org> writes: > David Rush <dr...@aol.net> wrote: > > No, but I've been whingeing about it in the last year or so. As far as > > your a) goes, I no longer use call-with-values/values in my own code, > > I pass the multiple-valued continuation directly.
> One point that doesn't get much play in the multiple values debates is > the zero values case. When I write a side effecting procedure, I end > it with a (values) call. ... Using cps seems unreasonably heavy > handed in this simple situation.
ISTM that (values) is similiarly heavy-handed.
> An > oxymoronic "no-value" value to satisfy "functional" semantics for a > non-function seems stilted compared to zero values which seems to > exactly capture the situation. How did you handle this case after you > jetisoned multiple values from your life?
I actually try to make sure that all functions have a meaningful return value, even is their primary purpose in life is side-effecting. I found that anything else started causing me major performance problems on type-sensitive compilers (read Stalin). The big problem with (values) is that it doesn't have a well-defined type, although I will grant you that it is better than my occasional lazy practice of not even paying attention to the return value of a side-effecting procedure.
david rush -- He who would make his own liberty secure, must guard even his enemy from repression -- Thomas Paine
felix <fe...@proxima-mt.de> wrote: > This is not necessarily working on R5RS compliant implementations, > if the call-site does not expect multiple values.
Ya, I know I'm getting a little off the R5RS track with this.
It seems like in an language embracing multiple values (and clearly r5rs isn't firmly in that category), it would make most sense for the continuations of the leading body statements to take an arbitrary number of values. I think that expecting zero values would actually make more sense than expecting one value, since any leading body statements are obviously being used purely for their side effects, not their value(s).
David Rush <dr...@aol.net> wrote: >> Using cps seems unreasonably heavy handed in this simple situation.
> ISTM that (values) is similiarly heavy-handed.
Um, not in terms of syntax at the call site!, but perhaps you are talking about the 'burden' of (values) itself.
> I actually try to make sure that all functions have a meaningful > return value, even is their primary purpose in life is > side-effecting. I found that anything else started causing me major > performance problems on type-sensitive compilers (read Stalin).
Hmm, I don't know much about Stalin, but if that is the case, then perhaps Stalin isn't modeling procedure return the values' types correctly. How is {[int-t int-t] -> [bogus-t]} any worse than {[int-t int-t] -> []}?
(That wasn't a retorical question, I'd be interested to know why multiple result types would be any different than multiple parameter types. Is it something to do with the fact that procedures have only one entry signature, but possible many continuation signatures? If so, how would single return values simplify that situation for the analysis?)
Anthony Carrico <acarr...@memebeam.org> writes: > David Rush <dr...@aol.net> wrote: > > I actually try to make sure that all functions have a meaningful > > return value, even is their primary purpose in life is > > side-effecting. I found that anything else started causing me major > > performance problems on type-sensitive compilers (read Stalin).
> Hmm, I don't know much about Stalin, but if that is the case, then > perhaps Stalin isn't modeling procedure return the values' types > correctly. How is {[int-t int-t] -> [bogus-t]} any worse than {[int-t > int-t] -> []}?
Well, for one Stalin only supports R4RS. secondly, the R5RS DS has two bottom values specified in it, #unspecified and #undefined. The set! operator (and possibly all of the mutation operators, IIRC) have type
location * object -> #unspecified
Stalin just treats those bottom types as _|_ for the purposes of type (and representation) inference. When they get unified with some other type they just vanish. Actually, I'm probably wrong, but that's how I'd do it. (Actually, I wouldn't, I'd reify #unspecified, but that's another rant...)
> (That wasn't a retorical question, I'd be interested to know why > multiple result types would be any different than multiple parameter > types.
we need to be clearer about the terms of this discourse. Does multiple parameter types mean
type1 * type1 -> type3
or
(con1 type1 | con2 type2) -> type3
?
If it's the first, there is no difference, hence the proposal which has been re-opened about sequence types. In the second case, there is a mandatory type dispatch that's going to occur somewhere. This type dispatch causes extra overhead in terms of both storage and CPU cycles.
> Is it something to do with the fact that procedures have only > one entry signature, but possible many continuation signatures?
Indirectly. The real difficulty is that you can't know the return arity of a procedure without traversing the entire call-graph of the procedure. For a whole-program compiler, this is just another increment to the compile-time in order to achieve (ISTM) very little added expressivity. For separate compilation, it is a significant problem which creates run-time overhead. Others have argued that the overhead can be minimized, but I don't see enough value in the construct in the first place to care.
david rush -- He who would make his own liberty secure, must guard even his enemy from repression -- Thomas Paine
David Rush <dr...@aol.net> wrote: > Anthony Carrico <acarr...@memebeam.org> writes: >> David Rush <dr...@aol.net> wrote: >> > I actually try to make sure that all functions have a meaningful >> > return value, even is their primary purpose in life is >> > side-effecting. I found that anything else started causing me major >> > performance problems on type-sensitive compilers (read Stalin).
>> Hmm, I don't know much about Stalin, but if that is the case, then >> perhaps Stalin isn't modeling procedure return the values' types >> correctly. How is {[int-t int-t] -> [bogus-t]} any worse than {[int-t >> int-t] -> []}?
> Well, for one Stalin only supports R4RS.
If I remember correctly, there aren't multiple values in R4RS, so compatibility with R4RS would be a very good reason to avoid multiple values.
>> I'd be interested to know why multiple result types would be any >> different than multiple parameter types.
> we need to be clearer about the terms of this discourse. Does multiple > parameter types mean
> type1 * type1 -> type3 {A}
> or
> (con1 type1 | con2 type2) -> type3 {B}
Umm... {A}, but actually I don't understand {B}. What are "con1" and "con2"? I could understand "type1 | type2 -> type3", that is how I would describe the situation created by "many continuation signatures" (with or without multiple values).
> If it's the first, there is no difference,
Great: if there is no difference then it shouldn't be any more trouble for the analysis to do-what-it-does for "multiple values" as it does for "multiple parameters".
> hence the proposal which has been re-opened about sequence types.
No. Only if the values (and parameters, I gather, for the ML fans) where exclusively sequences, but if we had the option of passing (aka returning) sequences and non-sequences, then it would be a different thing.
That's why this talk about sequences scares me enough to speak up. Think about the continuations for the operator and arguments of a call. I'm fine in the multiple-values-world where one value must be returned/passed to these. I'm fine in the nice-sequences-world where a sequence of length one must be returned/passed to these (but I think the syntax would suck) . I'm freaked out in the whacked-sequences-world where one of anything is fine, sequence or not -- I just can't make any sense of a type system like that.
The whacked-sequences-world seems like the warped appropriation of some functional pincipal into the wrong context, like somebody skipped some level of abstraction, like a subtle but deadly mistake, like something that would be impossible to analyze or compile efficiently. I'm confused, since over the years I've seen the position advocated by respected people who are orders of magnitude more in-the-know than myself; I must be in the wrong, but I can't figure out where I'm in the wrong!
> ... I don't see enough value in the construct in the first place to > care.
And so it's back to syntax. In engineering terms I see a lot of value. With multiple parameters and multiple values you are making a distinction between objects and control flow that you aren't making without them. It would be good news if that valuable distinction didn't make any difference to the math and the compiler.
Anthony Carrico wrote: > That's why this talk about sequences scares me enough to speak > up. Think about the continuations for the operator and arguments of a > call. I'm fine in the multiple-values-world where one value must be > returned/passed to these. I'm fine in the nice-sequences-world where a > sequence of length one must be returned/passed to these (but I think > the syntax would suck) . I'm freaked out in the > whacked-sequences-world where one of anything is fine, sequence or not > -- I just can't make any sense of a type system like that.
Isn't this exactly what ML does with tuples, though? Or are you saying that you think that ML can get away with that because of static typing? Still, given the ML function "fun foo x = x", we can do any of these:
Does that qualify as "whacked-tuples" in your book? Or do you see a difference in the Scheme case?
The proposal as Clinger described it actually sounded pretty attractive to me. I avoid using multiple values as much as possible, partly because I dislike their non-first-class-ness and find them unwieldy, but I would use first-class tuples/sequences if they were available.
In case it hasn't already been mentioned, sequences as Clinger described them are already present in the Scheme formal semantics, since they are needed internally. The proposal would provide a first-class type that mirrors a type already present in and central to the semantics.
I somehow doubt that you have to be concerned about this getting adopted any time soon, though...
Anthony Carrico <acarr...@memebeam.org> writes: > David Rush <dr...@aol.net> wrote: > > Anthony Carrico <acarr...@memebeam.org> writes: > >> David Rush <dr...@aol.net> wrote: > > Well, for one Stalin only supports R4RS.
> If I remember correctly, there aren't multiple values in R4RS, so > compatibility with R4RS would be a very good reason to avoid multiple > values.
Gambit is also only R4RS, which means two of the top four most efficient code-generating Scheme compilers don't support it directly. This was the reason why I started writing call/values-free code. Then I found out that it wasn't painful, and that it was in some ways better.
> >> I'd be interested to know why multiple result types would be any > >> different than multiple parameter types.
> > we need to be clearer about the terms of this discourse. Does multiple > > parameter types mean
> Umm... {A}, but actually I don't understand {B}. What are "con1" and > "con2"?
A bad attempt at approximating the SML
datatype Foo = con1 of type1 | con2 of type2
which would result in a type signature of
Foo -> type3
> > If it's the first, there is no difference,
> Great: if there is no difference then it shouldn't be any more trouble > for the analysis to do-what-it-does for "multiple values" as it does > for "multiple parameters".
I agree completely. In a purely theoretical sense functions have only one argument type and one return type. Scheme pragmatics are somewhat different.
> > hence the proposal which has been re-opened about sequence types.
> No. Only if the values (and parameters, I gather, for the ML fans) > where exclusively sequences, but if we had the option of passing (aka > returning) sequences and non-sequences, then it would be a different > thing.
I haven't read the actual proposal, only OP's summary thereof. I was waiting unutil the heavyweights weighed in with some commentary, since I am also in the midst of a severe production system crisi at the moment. Perhaps I'll get a chance to read it this week.
> Think about the continuations for the operator and arguments of a > call. I'm fine in the multiple-values-world where one value must be > returned/passed to these. I'm fine in the nice-sequences-world where a > sequence of length one must be returned/passed to these (but I think > the syntax would suck) . I'm freaked out in the > whacked-sequences-world where one of anything is fine, sequence or not
That does seem odd; however,
> -- I just can't make any sense of a type system like that.
Well, it makes sense in a backwards-compatibility kind of way. Up until R[45]RS Scheme seems to have avoided worrying about those sorts of issues (NIL is not #f! Long live #f!), but in the intervening years, people have written a fair bit of code. It seems there is less motivation to change.
The point being that since Scheme is dynamically typed, type checks are delayed until (primitive) function application. An arity 1 function, be it a normal application or a continuation is still an arity 1 function. If you apply a non-sequence operator to a sequence value you've written an erroneous program; however, you may write a program that has a single interface which has both a sequence and non-sequence calling convention (perhaps conditioned on some global state). That's pretty icky, but the Scheme car doesn't actually cons with seatbelts, so...
> I'm confused, since over the years I've seen the position > advocated by respected people who are orders of magnitude more > in-the-know than myself; I must be in the wrong, but I can't figure > out where I'm in the wrong!
Not necessarily. They may just be willing to make a different set of trade-offs than you.
> > ... I don't see enough value in the construct in the first place to > > care.
> And so it's back to syntax. In engineering terms I see a lot of > value. With multiple parameters and multiple values you are making a > distinction between objects and control flow that you aren't making > without them.
Assuming I understand you (which I'm not entirely sure about), yes. In engineering terms, R4RS Scheme already had multiple values, you just had to explicitly inject the continuation. R5RS added a crude syntax (over which people have built much nicer constructs) which actually hides the fundamental operation.
> It would be good news if that valuable distinction > didn't make any difference to the math and the compiler.
I don't think it makes a lot of difference to the math. Practically, it can mean a fair amount of difference to the compiler and run-time systems.
david rush -- He who would make his own liberty secure, must guard even his enemy from repression -- Thomas Paine
David Rush <dr...@aol.net> writes: > I haven't read the actual proposal, only OP's summary thereof. I was > waiting unutil the heavyweights weighed in with some commentary,
I'm not the heavyweight you are looking for, but since the "OP" specifically mentioned me, I want to thank him (her?) for giving an excellent summary of my original complaints.
To admit the truth, back in '95 I did not fully appreciate (or understand?) Will Clinger's proposal. As usual, when you don't bother to understand something reasonable, you are bound to reinvent it:
I still don't have any hope that something like this will get adopted into Scheme anytime soon (nor do I really care). But I thought that the reference might be interesting to some...
Matthias Blume <matth...@shimizu-blume.com> writes: > I'm not the heavyweight you are looking for, but since the "OP" > specifically mentioned me, I want to thank him (her?) for giving an > excellent summary of my original complaints.
You're welcome. =)
My motivation was simply that you gave exceptionally clear expression to my own rather inchoate misgivings about the call-with-values approach. The only missing piece of the puzzle was your initial lack of response to William Clinger's subsequent proposal. The pieces are starting to come together, though.
> To admit the truth, back in '95 I did not fully appreciate (or > understand?) Will Clinger's proposal. As usual, when you don't bother > to understand something reasonable, you are bound to reinvent it:
I found this a day or two after my original post, thanks to a reference into the thread cited by Matthias Radestock. That thread was most enlightening and tackled a lot of thorny issues.
However, as a result of reading Clinger's original proposal, Matthias Radestock's proposal in <3CB962FD.4030...@sorted.org>, and yours, I'm left with a picture of their relative differences that's less than clear. There seem to be concerns about first-classness and [x] = x winding through the various arguments people have put forward, and I'm quite curious to see where things stand now. Do any of these proposals seem more attractive to people than the others? Are they really just different ways of presenting the same well-defined notion?
Most importantly, can everyone agree on the satisfactoriness of this (or one of these) ideas *in principle* as a sounder alternative to R5RS values/call-with-values?
I realise this kind of broad agreement is a very elusive goal in the Scheme world, especially for such a radical (to use Will Clinger's word) proposal. But after seeing people beat on the question and the various answers (for more than eight years!), it really seems like we have something good here, and I didn't want to see it vanish into the bit-bucket because we've all since poured enough syntactic sugar on top of call-with-values to avoid thinking too much about how unpleasant it is to have such an artifact in the Revised Report.
Sometimes radical corrections are appropriate when a freak storm throws the ship off course. I use the analogy advisedly given what seems to be the very questionable history of how values/call-with-values actually made it into the language.
I also think we're very lucky to have in this proposal a radical correction which doesn't break any existing code.
If philosophical agreement can be reached, the only outstanding question is how implementors feel about it. I was hoping some of them would chime in to say that they find the idea totally unworkable, utterly trivial, or something in between.
Although the proposal requires "changes to all existing implementations of Scheme"---how serious are those changes, really?
> I still don't have any hope that something like this will get adopted > into Scheme anytime soon (nor do I really care).
Thanks for reaffirming your support for this idea. I know you don't care, but I do. It will be the death of me, no doubt. =)
Category 5 <catfive...@chaosnet.org> writes: > However, as a result of reading Clinger's original proposal, Matthias > Radestock's proposal in <3CB962FD.4030...@sorted.org>, and yours, I'm > left with a picture of their relative differences that's less than > clear. There seem to be concerns about first-classness and [x] = x > winding through the various arguments people have put forward, and I'm > quite curious to see where things stand now. Do any of these proposals > seem more attractive to people than the others? Are they really just > different ways of presenting the same well-defined notion?
I think that everything is well-defined. Having said this, however, I do understand some of the misgivings that people have about the approach. Everything really comes down to the question of whether it is acceptable that "[x] = x".
A few remarks about this:
0. There are no phisosophical problems with [x] = x. After all, [x] is certainly isomorphic to x, so we might as well identify the two. Also, notice that even if we don't have tuples with such behavior, I can already define (in today's Scheme) a tupling function with this behavior:
1. Contrary to what many Lispers think, I don't think that most people would have serious problems with having rules such as [x] = x. After all, we do this all the time in math and in (other) programming languages where placing extra parentheses does not alter the semantics. However, in Scheme (and any other Lisp), this is indeed rather unnatural given that (x) and x are certainly not identical, neither as data nor as code.
2. On the practical side, whether or not having such things is problematic is certainly in the eye of the beholder. For most programming tasks, the issue will not be noticable at all. However, if tuples end up being used like lists or vectors (meaning: people write code that iterate over the elements, etc.), then the "inconsistency" will be very annoying. Given that there are already perfectly good sequence types without this problem, I don't see this as too much of an issue, though.
I would not want to have this behavior for lists, vectors, or strings. Modula-2, iirc, made the mistake of using "c" as a notation for the character c while string literals of any other length (0, >1) were taken as notation for a different type. During my brief encounters with the language, I found this very disturbing. Similarly bad, SML'90 did not have a char type and used single-char strings in their place. (In all fairness, at least the types were the same for all string literals, though.)
Also, it is worth remembering the point I made in point 0: people can already create all kinds of "inconsistent" behavior in their code, even for the set of currently available sequence types, so 100% "generic" is not possible for those either.
3. Meta-programming (including macros) can, as some have pointed out, get tricky if the meta-level code wants to do something generic for argument lists of various lengths. (This is simply an instance of the "treat tuples as vectors or lists" scenario.)
Personally, I am not a huge fan of meta-programming in general or macros in particular, so I don't see this as a problem for me. But since macros are a big deal in Scheme now, it will probable get in some people's way.
In many cases there will be easy workarounds. Example: My "ml-nlffigen" program generator tool (generating SML code for interfacing C libraries) uses a hack where information about C function types is encoded in SML (phantom-)types. The arguments are encoded as an SML tuple type. To avoid the pathological tuple-of-one case, I simply added an otherwise unused element ("unit") to the encoding to make sure that there are always at least two elements... (Of course, a hack like this is only possible because the interface in question is not exposed to anyone other than the program generator tool.)
"Category 5" <catfive...@chaosnet.org> wrote: > Most importantly, can everyone agree on the satisfactoriness of this (or > one of these) ideas *in principle* as a sounder alternative to R5RS > values/call-with-values?
My $0.02, as a data point: the idea appeals to me, and certainly sounds preferable to the current multiple values mechanism. But because it has broader implications, I'd want to play with it in a toy implementation before I would check a box that said "switch Scheme to use tuples". While I like the way ML works in this area, I wouldn't trust my ability to map that onto Scheme, in my head.
> I also think we're very lucky to have in this proposal a radical > correction which doesn't break any existing code.
I agree, with the caveat above. Which brings me to a point about implementing this: given the nature of this change, couldn't a Scheme-with-tuples be used fairly "easily" to implement a perfectly standard R5RS Scheme, with parameter lists and R5RS multiple values implemented in terms of tuples? If so, such an implementation would essentially appear to provide an extension to R5RS in the form of first-class tuples, even though internally, tuples would be the more fundamental feature.
If this is possible, it also means that the proposal could begin life as a SRFI, providing a possible way to migrate in this direction.
> Although the proposal requires "changes to all existing implementations > of Scheme"---how serious are those changes, really?
If you're not implementing it, you shouldn't downplay it. I suspect this would be a fairly serious change in any real Scheme implementation.
Anton van Straaten <an...@appsolutions.com> wrote:
> Isn't this exactly what ML does with tuples, though? ... > foo 4 => 4 : int > foo (4) => 4 : int
If you say so, it must be true (I'm not an ML programmer, I only know what I picked up from Appel's "Compiling with Continuations", and these threads). Thanks for clearing that up. I went back and read Clinger's 2 Feb 1995 proposal; it uses this same idea. I don't know if Clinger was being serious or sarcastic, but I'll take it seriously for a minute.
From Clinger: (eq? '[[[[foo]]]] 'foo) => #t
> Does that qualify as "whacked-tuples" in your book?
Yes it does (but I'm still listening).
I'm currently in the camp that thinks R5RS multiple values already make perfect sense. They seem to jive 100% with my intuition that Scheme is really just a convenient direct style syntax for CPS. From that point of view continuations can have multiple results because continuations are just lambdas and lambdas can have multiple parameters. How could this be controversial?
The only value I see in the "whacked-tuples" model is it makes things nice for people who happen to want to write a procedure whose mode of operation is to pass along an arbitrary number of results from one procedure to the corresponding parameters of another. This particular control flow isn't even possible in the functional world, so why is anyone interested in calling it "compose"? It seems to me that people are trying to generalize something simple (compose in the world of functions with one parameter and one result) into a context where it doesn't make any sense. So let's call that compose-with-a-twist. Now, What makes compose-with-a-twist so important that we should worry about it?
The logic: 1. Scheme has multiple values. 2. Let's extend compose to multiple values. 3. Hmm... this seems a little strange. 4. Let's redesign Scheme's call mechanism.
I submit that the error may have been made in step 2!
If people still want to continue with step 4, at least consider the following: Clinger showed that there is no price to be payed for multiple values (when you aren't using them). To my knowledge he didn't show that there is no price to be payed for sequences (aka tuples).
In particular, who's job is it to unwind all those tuples (aka sequences) of one? The tuple constructors? The accessors? During the normal course of program flow, who's job is it to make sure they don't build up in the first place? Are they a space leak in certain unfortunate code?
The other thing I find strange about tuples is that tuple constructors must be "special" procedures since they take more than one argument in a one argument only world.
Thanks everyone for an interesting and thoughtful thread.
> Anton van Straaten <an...@appsolutions.com> wrote: > > Isn't this exactly what ML does with tuples, though? > ... > > foo 4 => 4 : int > > foo (4) => 4 : int
> If you say so, it must be true (I'm not an ML programmer, I only know > what I picked up from Appel's "Compiling with Continuations", and > these threads). Thanks for clearing that up. I went back and read > Clinger's 2 Feb 1995 proposal; it uses this same idea. I don't know if > Clinger was being serious or sarcastic, but I'll take it seriously for > a minute.
> From Clinger: > (eq? '[[[[foo]]]] 'foo) => #t
> > Does that qualify as "whacked-tuples" in your book?
> Yes it does (but I'm still listening).
> I'm currently in the camp that thinks R5RS multiple values already > make perfect sense. They seem to jive 100% with my intuition that > Scheme is really just a convenient direct style syntax for CPS. From > that point of view continuations can have multiple results because > continuations are just lambdas and lambdas can have multiple > parameters. How could this be controversial?
> The only value I see in the "whacked-tuples" model is it makes things > nice for people who happen to want to write a procedure whose mode of > operation is to pass along an arbitrary number of results from one > procedure to the corresponding parameters of another. This particular > control flow isn't even possible in the functional world, so why is > anyone interested in calling it "compose"? It seems to me that people > are trying to generalize something simple (compose in the world of > functions with one parameter and one result) into a context where it > doesn't make any sense. So let's call that compose-with-a-twist. Now, > What makes compose-with-a-twist so important that we should worry > about it?
Three words: data directed programming.
If you have a DD problem, using an OO language is a pain in the butt (it always wants an inheritance hierarchy which is seldom mirrored by anything intrinsic to the problem) and using a functional language is okay but suboptimal. So DD programming is usually done in imperative languages like C or Pascal.
But the ability to pass arbitrary multiple results directly in as parameters to another function would make scheme into a better DD language than any now extant.
The basic problem when you do DD programming in an imperative language is that you wind up having to manage composition of procedures with scores of parameters and scores of return values (which, in C, you implement as parameters passed by reference).
Structurally speaking, it's not particularly hard to grasp, but it's so damn easy to get lost in the parameter lists and so damn easy to get off by one when you're counting arguments, that anything you could use to elide the lists (and also efficiently pass the extra parameters on the stack) would be deeply welcome and liberating to people doing DD programming.
I know, it's one layer lower than OO and functional languages by nature can go a couple layers higher; but some problems really are best solved a level below OO, just as some are best solved using OO methods and some are best solved using Unification methods.