Newsgroups: comp.lang.scheme
Path: cantaloupe.srv.cs.cmu.edu!das-news2.harvard.edu!news2.near.net!howland.reston.ans.net!swrinde!sdd.hp.com!news.cs.indiana.edu!dyb@cs.indiana.edu
From: "R. Kent Dybvig" <dyb@cs.indiana.edu>
Subject: Re: giving up (Was: Re: Explanation about call-with-values)
Message-ID: <1995Jan26.174431.9717@news.cs.indiana.edu>
Organization: Computer Science, Indiana 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>
Date: Thu, 26 Jan 1995 17:44:27 -0500
Lines: 94

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

>Even if values/call-with-values takes zero time their numbers seem to
>indicate that the original program spent 20% of its time returning
>from functions having multiple resturn values!  This sounds fairly
>unbelievable to me.  Or did they rewrite those passes to take
>advantage of values/call-with-values?  (In this case the numbers don't
>mean anything, because we are not comparing the same program anymore.)

Unbelievable as it may be, it was the case.  We were using macros for
call-with-values and values.  The macros chose between a list or vector
representation depending upon the number of values to be returned or
accepted.  (The macro for call-with-values required that the consumer
be a lambda expression.)  The only rewriting we did was to eliminate
the macros and use the new built-in versions of call-with-values and
values.

Since most of the computation in the concerned passes was performed in
registers, the memory operations involved in creating the lists and
vectors and taking them apart added up to a significant portion of the
total run time.  And with our implementation call-with-values and
values now do take essentially zero time (for integrable calls where
the consumer is a lambda expression).

>   Not so.  Lists and vectors are first class.

>Exactly.  It is against the spirit of Scheme -- as I understand it,
>and as far as the language still has one -- to have a procedure
>(`values') to construct second-class objects.  I still don't
>understand why first-class-ness prevents us from taking advantage of
>Wright's soft typing or similar kinds of analyses.

It isn't against the spirit or reality of Scheme.  The values are still
first class, we just aren't committing to any sort of container for
them.  Single return values don't come in a first-class container
either, but you don't complain that they aren't first class.  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).

>I do not understand why we suddenly give up on the goal of conceptual
>simplicity, and the goal of power through orthogonality.  In my eyes,
>`values' is a performance hack and as such it doesn't belong into a
>language like Scheme.

Perhaps.  I fought against multiple return values along these lines,
but I was finally convinced to agree to them based on arguments of
portability and readability of code.  Multiple return values do make
some code much more readable, and there's no doubt that they can
eliminate a lot of consing if implemented properly.

> ... `values' is a blatant symptom of feature cancer.  It is
>easy to define in other terms:

>        (define values list)
>        (define (call-with-values thunk receiver)
>          (apply receiver (thunk)))

>The properties of this pair of functions contain those of the proposed
>values/call-with-values. ...

It's not this simple, since this doesn't handle reified continuations
of arities other than one.  You need something like the following (from
the LFP paper):

(define values)
(define call-with-values)
(let ((magic (cons 'multiple 'values)))
  (define magic?
    (lambda (x)
      (and (pair? x) (eq? (car x) magic))))

  (set! call-with-current-continuation
    (let ((primitive-call/cc
           call-with-current-continuation))
      (lambda (p)
        (primitive-call/cc
          (lambda (k)
            (p (lambda args
                 (k (apply values args)))))))))

  (set! values
    (lambda args
      (if (and (not (null? args)) (null? (cdr args)))
          (car args)
          (cons magic args))))

  (set! call-with-values
    (lambda (producer consumer)
      (let ((x (producer)))
        (if (magic? x)
            (apply consumer (cdr x))
            (consumer x))))))
