Newsgroups: comp.lang.scheme
Path: cantaloupe.srv.cs.cmu.edu!das-news2.harvard.edu!news2.near.net!news.mathworks.com!zombie.ncsc.mil!news.duke.edu!godot.cc.duq.edu!hudson.lm.com!news.pop.psu.edu!psuvax1!news.ecn.bgu.edu!siemens!princeton!news.princeton.edu!blume
From: blume@dynamic.cs.princeton.edu (Matthias Blume)
Subject: Re: Explanation about call-with-values
In-Reply-To: djello@well.sf.ca.us's message of 24 Jan 1995 12:08:31 GMT
Message-ID: <BLUME.95Jan24114128@dynamic.cs.princeton.edu>
Originator: news@hedgehog.Princeton.EDU
Sender: news@Princeton.EDU (USENET News System)
Nntp-Posting-Host: dynamic.cs.princeton.edu
Organization: Princeton University
References: <3fqdq2$9g0@nkosi.well.com> <BLUME.95Jan21131442@dynamic.cs.princeton.edu>
	<3ft107$fgt@nkosi.well.com>
	<BLUME.95Jan22133847@dynamic.cs.princeton.edu>
	<3g004e$7ps@nkosi.well.com>
	<BLUME.95Jan23140314@dynamic.cs.princeton.edu>
	<3g2qjv$gdo@nkosi.well.com>
Date: Tue, 24 Jan 1995 16:41:28 GMT
Lines: 108

In article <3g2qjv$gdo@nkosi.well.com> djello@well.sf.ca.us (Darius Bacon) writes:

   >The difference in usefulness between a
   >scheme where `wrong-number-of-values' errors are caught at the time
   >those values arrive at the receiving function or at the time this
   >function actually tries to access them seems to be marginal at most.

   More than marginal, IMO; it's common for the receiving procedure to pass
   the value on or store it in a data structure, making the damage show up far
   from its source.

Huh?  How can this happen?  There are only two things you can possibly
do with the result of `values':  you can pass it on to your caller by
having it in tail position or you can take it apart.  Passing it on
will never trigger an error, neither in the `values' scheme nor in the
`list' scheme.  Taking it apart will *always* trigger the error: both
in the `call-with-values' scheme and in the `apply' scheme.

BTW, even though values/call-with-values is so similar to list/apply
it has one major disadvantage:  the thing constructed by `values' is
only second-class.  This is clearly against the spirit of Scheme, and
on this basis alone I find it despicable.  What I mean is this:

With list/apply I can say:

     (define (g ...)
	(let ((r (f ...)))
          <do something else>
          r))

where `f' can return `multiple' values by passing them as a list.  `g'
will return just the same set of multiple values, and it doesn't even
need to know about this fact.  But with `values' this wouldn't be
possible, because the continuation of the call to `f' only takes one
argument.  In this case `g' needs to be aware of the fact that `f'
returns multiple values and explicitely turn them into a temporary
data structure in order to deal with this fact.  If `f' is a parameter
to `g', then you arrive at the same kind of asymmetry you complained
about earlier in the context of my suggestion to use explicit
continuations.  Here you would have to write:

     (define (g ...)
       (call-with-values
	 (lambda () (f ...))
         (lambda r
           <do something else>
           (apply values r)))) 

which clearly takes away any advantages there might have been to using
`values' in the first place while also obfuscating the code.

`values' is clearly a symptom of `feature cancer', because it's
rationale is the opportunity for some optimization.  On top of that it
is un-schemely, the merits of the optimization it might enable are
questionable at best, and if they are worth something then they can
easily be achieved without a special new language feature.

   >In what sense is a `wrong number of arguments' type of error
   >preferable over `wrong argument to CAR'?  I really fail to see the
   >point here.

   If a values-record happens to be represented by a pair, then applying CAR
   to it is perfectly legal.

But if there aren't enough values in the list then CAR will be applied
to ().

   >If you want early error checking, then, please, make sure it can be
   >done at compile time.  The only reason for waiting until runtime seems
   >to be taking advantage of non-strictness, and there you really want to
   >delay errors as much as possible.

   I don't understand that last sentence.  If you'd care to post an expansion
   or a reference, I'd be interested.

`Non-strictness' refers to languages, where a non-terminating or
erroneous argument doesn't necessarily make the function call fail.
Strict languages correspond to the `applicative order evaluation' in
lambda calculus, which is how most people intuitively understand
function calls, but which also fails to compute certain normal forms
even if they exist.  Lazy languages use `normal order evaluation' and
compute the normal form of an expresssion whenever it exists.  To do
this they inherently take advantage of non-strictness (this is what it
is all about) by avoiding computations which might cause an error
unless they turn out to be absolutely necessary.

Most `strict' languages must include some non-strict special forms in
order to make the language usable.  For example, the expression

	(if (zero? y) 0 (/ x y))

takes advantage of the non-strictness of `if'.  If `if' would be
strict it would evaluate (/ x y) regardless of the outcome of the
(zero? y) test and trigger an error in the case of `y' being zero.

What I was saying was that delaying errors is most useful if you want
to take advantage of non-strictness.  In this context you want errors
to happen AS LATE AS POSSIBLE, because this way they might actually be
avoided altogether.

   Or the ML community, though they don't enjoy Lisp's wild freedom with
   regard to syntactic extension.  Oh, well.

That's my only complaint about ML, too.  Not enough consideration for
syntax.

--
-Matthias
