Newsgroups: comp.lang.scheme
Path: cantaloupe.srv.cs.cmu.edu!das-news2.harvard.edu!news2.near.net!news.mathworks.com!hookup!swrinde!howland.reston.ans.net!newsserver.jvnc.net!nntpserver.pppl.gov!princeton!news.princeton.edu!blume
From: blume@atomic.cs.princeton.edu (Matthias Blume)
Subject: Re: giving up (Was: Re: Explanation about call-with-values)
In-Reply-To: "R. Kent Dybvig"'s message of Sun, 29 Jan 1995 22:24:30 -0500
Message-ID: <BLUME.95Jan30110005@atomic.cs.princeton.edu>
Originator: news@hedgehog.Princeton.EDU
Sender: news@Princeton.EDU (USENET News System)
Nntp-Posting-Host: atomic.cs.princeton.edu
Organization: Princeton University
References: <BLUME.95Jan22133847@dynamic.cs.princeton.edu> <3g004e$7ps@nkosi.well.com>
	<BLUME.95Jan23140314@dynamic.cs.princeton.edu>
	<3g67ne$e4e@narnia.ccs.neu.edu>
	<BLUME.95Jan25165400@atomic.cs.princeton.edu>
	<1995Jan26.174431.9717@news.cs.indiana.edu>
	<BLUME.95Jan27114524@atomic.cs.princeton.edu>
	<1995Jan29.222433.21858@news.cs.indiana.edu>
Date: Mon, 30 Jan 1995 16:00:05 GMT
Lines: 181


Originally I didn't intend to prolong this thread any further.  But
since I have been accused of `missing the point' I have to answer.

In article <1995Jan29.222433.21858@news.cs.indiana.edu> "R. Kent Dybvig" <dyb@cs.indiana.edu> writes:

   blume@atomic.cs.princeton.edu (Matthias Blume) writes:

   >This whole discussion shows that multiple values are not just a
   >straight-forward extension to single values.  They are something
   >conceptually different.  They are containers!  As a matter of fact, I
   >would prefer (eq? (values 2) 2) to be #f, just like (eq? (list 2) 2)
   >is #f. 

   But in fact they are not containers.  There is no more a container for
   multiple than for single return values, just as there is no more a
   container for multiple than for single arguments.

And that's a pity.  ML (and other newer languages) get it right:  they
make every function take exactly one argument and produce exactly one
result.  Multiple arguments and multiple values are always passed in
containers, and we rely on good compilers to get rid of the overhead.
This allows for maximum orthogonality, and therefore for maximum
convenience.  `compose' in ML can be written as

	fun compose (f, g) = fn x => f (g x)

while in Scheme, in order to be fully general, we must write awkward
things like

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

And, btw, all `efficiency' benefits are lost too, because we had to
resort to a `catch-all' restlist argument `args' and `apply', which in
effect turns your `containerless' multiple arguments into an explicit
container.

   This is a point that
   you seem to have missed all along,

No, I have NOT missed that point all along.  I forgot who I replied to
with exactly what I wrote above, but I did so before.  Sorry, I just
didn't want to `shock' you with an even more `outrageous' demand like
getting rid of multiple arguments...

   and in missing it you have convinced
   yourself that multiple values are conceptually different from single
   values, even though we can write a cps conversion or denotational
   semantics (such as the one in the revised report) that shows that they
   are no more different than are multiple arguments from single arguments.

This is indeed true (not that I convinced myself about such a
difference, but that there is no such difference).  I was always very
much aware of this.

   >   Arguments
   >   don't come in first-class containers, although the syntax of lambda
   >   allows us to put them in one (a list).  And as you point out you can do
   >   the same thing with call-with-values (and with macros, you can make
   >   doing so very concise).

   >Actually, I would like to have all functions take just exactly one
   >argument.  If you want to pass more, then pass a container!  (Witness SML!)
   >This would make things symmetrical, and it would also settle your
   >complaint about list/apply not properly taking care of call/cc's behavior.

Ahh -- there we go.  I did say it before, so please don't acuse me of
missing any points here!  Thanks.

   So you would like multiple return values to be packaged in a container
   of some sort, and you would like the same for multiple arguments.

Yes.  Other languages do it to very good effect.  SML/NJ manages to
pass almost all arguments in registers, even though conceptually there
are no `multiple' arguments in ML.  With a good compiler it can be
done -- we don't need it at the language level.

If we want arbitrary restrictions and ad-hoc performance hacks then we
know where to find them.  They simply don't belong into the world of
Scheme or ML.  What we should thrive for is a proof, that conceptually
clean, simple, and powerful languages can be compiled efficiently
*despite* of their high-level nature.  By adding performance hacks like
`values' we are conceding defeat.

   I
   don't like the idea, and in fact think it goes in exactly the wrong
   direction for reasons I don't care to enumerate right now.

Oh -- that's great.  I completely disagree for reasons I don't care to
enumerate right now.

   (Some of my
   reasons were given in a September 1990 LASC article Bob Hieb and I
   wrote, called ``A new approach to procedures with variable arity'', in
   which we proposed an alternative to the current Scheme "rest" interface
   and, incidentally, a different multiple return values interface that I
   still prefer.)

I think that there is no reason whatsoever to have variable arity
procedures.  And I would like to see some of those reasons you don't
care to enumerate.

   Regardless, we aren't going to change the language to
   eliminate multiple-argument procedures (or the list-based rest
   interface, for that matter),

I know.  It is unfortunate that we seem to not take the necessary
steps of cleaning up the language while we take the unnecessary steps
of adding needless baggage.

   and the containerless multiple return
   values of the call-with-values/values interface is symmetric with the
   containerless multiple argument values.

Symmetric in its non-orthogonality.  What a feat!

   >Sometimes I might want to say:

   >	(call/cc
   >	  (lambda (k)
   >	    (let ((r (f ...)))
   >	      <do something else>
   >              (if (panic? ...) (k r))
   >              <do even more>
   >              r)))

   >I.e., I want to capture the return value(s) of the call to `f' and
   >pass them on to my continuation, and this should look the same no
   >matter whether my continuation takes one or many (or no) values.

   This is trivially accomplished with call-with-values and values:

     (call/cc
       (lambda (k)
	 (let ((r (call-with-values (lambda () (f ...)) list)))
	    <do something else>
	    (if (panic? ...) (apply k r))
	    <do even more>
	    (apply values r))))

Oh, sure!  I can also `trivially' code this into a Turing machine.  We
all know that Scheme with and without `values' is Turing-complete,
therefor you can do in one what you can do in the other and vice
versa.  Sorry, but I have to say that this is the most unconvincing
argument I've seen in a long time.

I didn't say it can't be done.  I said it can't be done in a
straight-forward and intuitive manner.  And even you must resort to
explicitly bundling up the values into a list.  If a compiler can
puzzle this apart and keep the values in registers, then it could have
done so in the first place without explicit new language constructs.
As a matter of fact it would be less likely for a compiler to keep all
values in separate registers in *your* example than it would be in
mine, because the implementor in a world with `values' will lazily rely
on the programmer to provide the optimizations.  If they are not there
then nothing will happen.

This all very much reminds me on the `register' keyword in C.

   This isn't quite as concise, but then in my experience it isn't often
   necessary to do this.

This is self-fulfilling prophecy:  Because it is so inconventient we
don't see much uses of it.  And because we see so very little uses we
don't need to support it conveniently...  That's the type of argument
we usually hear from those who oppose call/cc and such.

I'm very, very sad to see that I have to fight over these issues with
the biggest names in the Scheme community -- those who I would have
thought stand up for the principles layed down in the introduction to
R4RS.  But I guess those golden times are over.  I suggest to scrap
the first sentence in the next version of the report, because
apparently Scheme is not an example anymore of how languages can be
`designed not by piling feature on top of feature'...

--
-Matthias
