Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Scheme values and call-with-values

56 views
Skip to first unread message

Brad Lucier

unread,
Dec 3, 1997, 3:00:00 AM12/3/97
to

I've taken Will Clinger's little implementation of values and
call-with-values and started playing with it in Gambit-C.

I've been looking at CLIM-style code. In CLIM, point objects
must follow the point protocol, which basically guarantees only
that (point-position p) will return two values, the x and y
coordinates of a point. (There are default methods for point-x
and point-y that each call point-position, so it should be cheaper
to call point-position once than to call point-x and point-y
separately.)

There is a routine, make-3-point-transformation, that provides
an affine transformation that maps three given domain points to
three given range points, so it takes six point objects as arguments.
(Here domain and range mean the domain and range of the transformation.)

Similarly, the routine make-3-point-transformation* takes six
coordinates to specify three domain points and six coordinates to
specify three range points, so it has 12 arguments.

The only way I can figure out how to unwrap all these point objects
is to do something like

(define (make-3-point-transformation point-1 point-2 point-3
point-1-image point-2-image
point-3-image)
(call-with-values
(point-position point-1)
(lambda (x1 y1)
(call-with-values
(point-position point-2)
(lambda (x2 y2)
(call-with-values
(point-position point-3)
(lambda (x3 y3)
(call-with-values
(point-position point-1-image)
(lambda (x1-image y1-image)
(call-with-values
(point-position point-2-image)
(lambda (x2-image y2-image)
(call-with-values
(point-position point-3-image)
(lambda (x3-image y3-image)
(make-3-point-transformation*
x1 y1 x2 y2 x3 y3
x1-image y1-image x2-image y2-image x3-image
y3-image))))))))))))))

(define (make-3-point-transformation* x1 y1 x2 y2 x3 y3
x1-image y1-image x2-image
y2-image x3-image y3-image)
; continue with the real computations to construct the transformation

Is there a notationally less cumbersome way to unwrap the six point
objects using some combination of values and call-with-values?

I'm cross-posting this to comp.lang.lisp to see if the CL users would
like to comment on how to do something like this in Common Lisp.

Brad Lucier luc...@math.purdue.edu

Erik Naggum

unread,
Dec 3, 1997, 3:00:00 AM12/3/97
to

* Brad Lucier

| Is there a notationally less cumbersome way to unwrap the six point
| objects using some combination of values and call-with-values?
|
| I'm cross-posting this to comp.lang.lisp to see if the CL users would
| like to comment on how to do something like this in Common Lisp.

well, FWIW, here's how I would do it in Common Lisp:

(defun make-3-point-transformation (point-1 point-2 point-3
point-1-image point-2-image)
(multiple-value-call #'make-3-point-transformation*
(point-position point-1)
(point-position point-2)
(point-position point-3)
(point-position point-1-image)
(point-position point-2-image)))

#\Erik
--
if you think this year is "97", _you_ are not "year 2000 compliant".

see http://sourcery.naggum.no/emacs/ for GNU Emacs 20-related material.

Brad Lucier

unread,
Dec 4, 1997, 3:00:00 AM12/4/97
to

Brad Lucier wrote:

> Is there a notationally less cumbersome way to unwrap the six point
> objects using some combination of values and call-with-values?


I'm sorry, I misunderstood how to use values, it seems to be worse
than I thought. If point-position is defined, say, by

(define-method (point-position (p standard-point))
(values (point-x p) (point-y p)))

then the only way to use point-position is to wrap it in a thunk
and hand it to call-with-values. So the previous example should
have been (using shorter variable names ;-)

(define (make-3-point-transformation p1 p2 p3
ip1 ip2 ip3)
(call-with-values
(lambda () (point-position p1))
(lambda (x1 y1)
(call-with-values
(lambda () (point-position p2))
(lambda (x2 y2)
(call-with-values
(lambda () (point-position p3))
(lambda (x3 y3)
(call-with-values
(lambda () (point-position ip1))
(lambda (ix1 iy1)
(call-with-values
(lambda () (point-position ip2))
(lambda (ix2 iy2)
(call-with-values
(lambda () (point-position ip3))
(lambda (ix3 iy3)


(make-3-point-transformation*
x1 y1 x2 y2 x3 y3

ix1 iy1 ix2 iy2 ix3 iy3))))))))))))))

(define (make-3-point-transformation* x1 y1 x2 y2 x3 y3

ix1 iy1 ix2 iy2 ix3 iy3)
...

Since you can't use values for anything except being returned by
a thunk, would it make sense in Scheme that point-position not
return

(values (point-x p) (point-y p))

but

(lambda () (values (point-x p) (point-y p)))

?

Values seems to create a strange object. Is it correct to say,
for example

((lambda (x)
(call-with-values
(lambda () x)
+))
(values 1 2))

? (Clinger's code allows this, is it meant to allow this?)

Values would seem to create a closure (meaning in this
instance a vector-like object). Call-with-values would take the
result of calling its first argument, and if it's a closure then
call-with-values would pass the entries of the closure as arguments
to the second argument; if the result of calling the first argument
is not a closure, then call-with-values would pass that result
verbatim to the second argument.

I'm sorry if I just resaid in words what Clinger's code does, but
I could use more explanation of how values works. If they
are just vector-like objects, then why aren't there easy ways to
concatenate them into larger values? Why does one have to wrap
them in a thunk before they can be used? Why is the notation so
cumbersome? I have a feeling that this has been discussed
extensively by some group of people since R4RS came out;
perhaps someone can enlighten me.

Again I'm posting this to c.l.lisp in case the lisp people want
to explain how these issues are dealt with in CL. (Thanks to
Erik Naggum for his earlier response.)

Brad Lucier luc...@math.purdue.edu

Rolf-Thomas Happe

unread,
Dec 5, 1997, 3:00:00 AM12/5/97
to

In article <348726...@math.purdue.edu> Brad Lucier
<luc...@math.purdue.edu> writes:
then the only way to use point-position is to wrap it in a thunk
and hand it to call-with-values. So the previous example should
have been (using shorter variable names ;-)

Having shortened the variable names, you may also consider a syntactic
embellishment via syntax-rules. For example, Scheme48 let's you write

> (receive (x y z) (values 1 2 3) (+ x y z))
6

rthappe

--
Getretener Quark wird weich und breit. (Tucholsky)
http://www.cauce.org/ Coalition Against Unsolicited Commercial Email

Brad Lucier

unread,
Dec 5, 1997, 3:00:00 AM12/5/97
to

In e-mail, John Clements (clem...@cs.rice.edu) suggested mapping
values to lists to get more compact, and perhaps more readable, code.
For example, one could define

(define (values->list . args)
(apply append
(map (lambda (value)
(call-with-values
(lambda () value)
list))
args)))

(define (multiple-value-call f . args)
(apply f (apply values->list args)))



(define (make-3-point-transformation
point-1 point-2 point-3
point-1-image point-2-image point-3-image)

(multiple-value-call make-3-point-transformation*


(point-position point-1)
(point-position point-2)
(point-position point-3)
(point-position point-1-image)
(point-position point-2-image)

(point-position point-3-image)))

which seems to be the Common Lisp way.

But all these transformations to lists seem very inefficient. I guess
I'm trying to say that there should be better primitives available,
or why have multiple values in the first place?

Clements also pointed out how one might do something similar in ML.
Parallel to the ML way, one could define a macro in Scheme to translate,
e.g.,

(multiple-value-bind* (((x1 y1) (point-position point-1))
((x2 y2) (point-position point-2)))
(whatever x1 y1 x2 y2))

where each entry in multiple-value-bind* is a pair

(formal-parameter-list-k values-k)

to a series of nested call-with-values



(call-with-values
(lambda () (point-position p1))
(lambda (x1 y1)
(call-with-values
(lambda () (point-position p2))
(lambda (x2 y2)

(whatever x1 y1 x2 y2)))))

I call this multiple-value-bind* in parallel with let*, since the
bindings are made sequentially, rather than in parallel. I don't see
easily how a multiple-value-bind that corresponds to let could be
written using the values and call-with-values construct. (I don't know
the macro system in the appendix of R4RS; with define-macro it may be
possible by reproducing all the formal-parameter-lists with variable
names replaced by gensyms, calling multiple-value-bind* with the gensymed
names, and then binding all the real names to the gensyms in parallel.)

Not having multiple-value-bind could lead to problems. For example,

(let ((x 1))
(multiple-values-bind* (((x) (values 2))
(y (values x)))
(apply + (cons x y)))

would evaluate to 4, as it should, since the value of x in
multiple-values-bind* is bound before the value of y is bound. However,
it would seem to be very important to have a construct that binds x and
y in parallel, so that

(let ((x 1))
(multiple-values-bind (((x) (values 2))
(y (values x)))
(apply + (cons x y)))

would evaluate to 3 (i.e, the binding of the local x should not shadow
the enclosing binding of x).

At one time I snarfed a copy of r4.95rs.ps off the net, which I presume
indicates the current thinking about what R5RS should be. I found
multiple values mentioned only on page 32, and there is nothing there
other than values and call-with-values.

The basic question is about how to bind arguments to formal parameters
in the lambda calculus. Given the above considerations, I propose
that multiple-values-bind (or some construct like it) be the basic
construct for binding multiple arguments to formal parameters in lambda
lists. From this, one could construct multiple-values-bind*, and even
call-with-values. I know that the RkRS authors discuss all proposed
extensions to the language extensively, so I presume that this issue has
also been discussed. Perhaps someone from that group could enlighten
me about why they chose values and call-with-values.

Or, perhaps someone can show me how to write a macro using values and
call-with-values that allows me to write multiple-values-bind. However
the macro is written, it would seem to be necessary to expand it into a
nexted series of call-with-values. This implies to me that my understanding
that the various init expressions in a let can be evaluated in parallel
(I'm extending the semantics of "in some unspecified order" in
section 4.2.2 of R4RS, which may imply that they are computed *in
order*, rather than, more generally, in parallel) would not be possible
to observe from the expanded code. I guess I don't find the macro solution
a true solution if it can change the semantics, or even limit possible
code optimizations.


Alchemy Petrofsky

unread,
Dec 6, 1997, 3:00:00 AM12/6/97
to

> In e-mail, John Clements (clem...@cs.rice.edu) suggested mapping
> values to lists to get more compact, and perhaps more readable, code.
> For example, one could define
>
> (define (values->list . args)
> (apply append
> (map (lambda (value)
> (call-with-values
> (lambda () value)
> list))
> args)))
>
> (define (multiple-value-call f . args)
> (apply f (apply values->list args)))
>
> (define (make-3-point-transformation
> point-1 point-2 point-3
> point-1-image point-2-image point-3-image)
> (multiple-value-call make-3-point-transformation*
> (point-position point-1)
> (point-position point-2)
> (point-position point-3)
> (point-position point-1-image)
> (point-position point-2-image)
> (point-position point-3-image)))
>

You'll get unspecified behavior before multiple-value-call is called,
because the calls to point-position will try to return two values to a
continuation only expecting one. To quote the relevant portion of the
spec:

Except for continuations created by the call-with-values
procedure, all continuations take exactly one value, as now; the
effect of passing no value or more than one value to continuations
that were not created by call-with-values is unspecified (as
indeed it is unspecified now).

You can define the common lisp special forms multiple-value-list and
multiple-value-call as macros like so:

;; (multiple-value-list (values 1 2)) => (1 2)
(define-syntax multiple-value-list
(syntax-rules ()
((multiple-value-list form)
(call-with-values (lambda () form) list))))

;; (multiple-value-call + (values 1 2) 3 (values 4 5)) => 15
(define-syntax multiple-value-call
(syntax-rules ()
((multiple-value-call f form ...)
(apply f (append (multiple-value-list form) ...)))))

Your complaint still applies that it seems almost impossible for the
implementation to optimize away the list creations.

The common lisp multiple-value-bind, which takes only one
multiple-value-producing form, can be easily and efficiently defined
as:

;; (multiple-value-bind (x y) (values 1 2) (+ x y)) => 3
(define-syntax multiple-value-bind
(syntax-rules ()
((multiple-value-bind vars form body1 body2 ...)
(call-with-values (lambda () form) (lambda vars body1 body2 ...)))))

Your proposed multiple-values-bind* is also easily accomplished (I'm
going to call your proposed syntaxes let-values and let-values* to
avoid confusion with the common lisp forms):

;;(let ((x 1))
;; (let-values* (((x y) (values 2 3)) (z (values x 4)))
;; (apply + (append (list x y) z)))) => 11
(define-syntax let-values*
(syntax-rules ()
((let-values* () body1 body2 ...) (body1 body2 ...))
((let-values* ((vars val) binding ...) body1 body2 ...)
(multiple-value-bind vars val (let-values* (binding ...)
body1 body2 ...)))))

Defining let-values is pretty easy too, but, like multiple-value-call,
this does presumably-non-optimizable values-to-list translations:

;;(let ((x 1))
;; (let-values* (((x y) (values 2 3)) (z (values x 4)))
;; (apply + (append (list x y) z)))) => 10
(define-syntax let-values
(syntax-rules ()
((let-values () body1 body2 ...) (body1 body2 ...))
((let-values ((vars val) binding ...) body1 body2 ...)
(let ((temp (multiple-value-list val)))
(let-values (binding ...)
(apply (lambda vars body1 body2 ...) temp))))))

I think your proposed semantics for let-values are nice and clean and
I agree that making it standard would probably allow some
implementations to provide a better version of it than a program can
portably define by itself. Like you, however, I'm ignorant of the
discussion that preceeded the call-with-values proposal.

-al

P.S. My quote of "the spec" above is really just from the "The Scheme
of Things" article in Lisp Pointers V(4), October 1992, which is
available from the scheme repository <URL:http://www.cs.indiana.edu/
scheme-repository>. Where did you find r4.95rs?


P.P.S. For the sake of completeness, here's letrec-values:

;; (let ((a 1) (b 2)) (set-many! (a b) '(3 4)) (+ a b)) => 7
(define-syntax set-many!
(syntax-rules ()
((set-many! () l) #f)
((set-many! (var1 var2 ...) l)
(let ((temp l))
(set! var1 (car temp))
(set-many! (var2 ...) (cdr temp))))))

;; (letrec-values ((a b) (values 1 (lambda () a))) (b)) => 1
(define-syntax letrec-values
(syntax-rules ()
((letrec-values () body1 body2 ...) (body1 body2 ...))
((letrec-values ((var val) binding ...) body1 body2 ...)
(let ((var 'undefined))
(letrec-values (binding ...)
(set! var (multiple-value-list val)) body1 body2 ...)))
((letrec-values (((var ...) val) binding ...) body1 body2 ...)
(let ((var 'undefined) ...)
(letrec-values (binding ...)
(set-many! (var ...) (multiple-value-list val))
body1 body2 ...)))))


Almanac Petrofsky

unread,
Dec 6, 1997, 3:00:00 AM12/6/97
to

(Continuing the tradition in this thread of following up to one's own
posts...)

I just tried actually running the code from the previous post and
discovered a few bugs (which, of course, I should have done *before*
posting, but hey, it's just not usenet if you aren't shooting wildly
from the hip).

The versions below have been tested with kawa-1.5. They probably
still have bugs, but at the very least all the examples work as
advertised.

-al

;; (multiple-value-list (values 1 2)) => (1 2)
(define-syntax multiple-value-list
(syntax-rules ()
((multiple-value-list form)
(call-with-values (lambda () form) list))))

;; (multiple-value-call + (values 1 2) 3 (values 4 5)) => 15
(define-syntax multiple-value-call
(syntax-rules ()
((multiple-value-call f form ...)
(apply f (append (multiple-value-list form) ...)))))

;; (multiple-value-bind (x y) (values 1 2) (+ x y)) => 3
(define-syntax multiple-value-bind
(syntax-rules ()
((multiple-value-bind vars form body ...)
(call-with-values (lambda () form) (lambda vars body ...)))))

;;(let ((x 1))
;; (let-values* (((x y) (values 2 3)) (z (values x 4)))
;; (apply + (append (list x y) z)))) => 11
(define-syntax let-values*
(syntax-rules ()

((let-values* () body ...) (begin body ...))
((let-values* ((vars val) binding ...) body ...)
(multiple-value-bind vars val (let-values* (binding ...) body ...)))))

;;(let ((x 1))
;; (let-values (((x y) (values 2 3)) (z (values x 4)))


;; (apply + (append (list x y) z)))) => 10
(define-syntax let-values
(syntax-rules ()

((let-values () body ...) (begin body ...))
((let-values ((vars val) binding ...) body ...)


(let ((temp (multiple-value-list val)))
(let-values (binding ...)

(apply (lambda vars body ...) temp))))))


;; (let ((a 1) (b 2)) (set-many! (a b) '(3 4)) (+ a b)) => 7
(define-syntax set-many!
(syntax-rules ()
((set-many! () l) #f)
((set-many! (var1 var2 ...) l)
(let ((temp l))
(set! var1 (car temp))
(set-many! (var2 ...) (cdr temp))))))

;; (letrec-values (((a b) (values 1 (lambda () a)))) (b)) => 1
(define-syntax letrec-values
(syntax-rules ()
((letrec-values () body ...) (begin body ...))
((letrec-values (((var ...) val) binding ...) body ...)


(let ((var 'undefined) ...)
(letrec-values (binding ...)

(set-many! (var ...) (multiple-value-list val)) body ...)))
((letrec-values ((var val) binding ...) body ...)
(letrec-values (((var) (multiple-value-list val)) binding ...) body ...))))

Lars Thomas Hansen

unread,
Dec 6, 1997, 3:00:00 AM12/6/97
to

From: Alchemy Petrofsky <alba...@wco.com>
Message-ID: <877m9iw...@albatros.wco.com>

>Defining let-values is pretty easy too, but, like multiple-value-call,
>this does presumably-non-optimizable values-to-list translations:
>

> ;;(let ((x 1))
> ;; (let-values* (((x y) (values 2 3)) (z (values x 4)))

> ;; (apply + (append (list x y) z)))) => 10
> (define-syntax let-values
> (syntax-rules ()

> ((let-values () body1 body2 ...) (body1 body2 ...))
> ((let-values ((vars val) binding ...) body1 body2 ...)


> (let ((temp (multiple-value-list val)))
> (let-values (binding ...)

> (apply (lambda vars body1 body2 ...) temp))))))

I belive the following definition does the right thing, and it does not
require the intermediate list.

(define-syntax let-values
(syntax-rules ()
((let-values (binding ...) body1 body2 ...)
(let-values "bind" (binding ...) () body1 body2 ...))
((let-values "bind" () temps body1 body2 ...)
(let temps body1 body2 ...))
((let-values "bind" (((var ...) val) binding ...) t body1 body2 ...)
(let-values "bind1" (var ...) val () t (binding ...) body1 body2 ...))
((let-values "bind1" () val temps t bindings body1 body2 ...)
(call-with-values
(lambda () val)
(lambda temps
(let-values "bind" bindings t body1 body2 ...))))
((let-values "bind1" (v1 v2 ...) val (temp ...) (t ...)
bindings body1 body2 ...)
(let-values "bind1" (v2 ...) val (temp ... x) ((v1 x) t ...)
bindings body1 body2 ...))
((let-values "bind" ((var val) binding ...) (t ...) body1 body2 ...)
(let ((tmp val))
(let-values "bind" (binding ...) ((var tmp) t ...) body1 body2 ...)))
))

I think this conforms to the R4.95RS, but I'm not able to check that
right now.

Testing:

> (let ((x 1))
(let-values (((x y) (values 2 3)) (z (values x)))
(list x y z)))
(2 3 1)
> (pretty-print
(macro-expand '(let ((x 1))
(let-values (((x y) (values 2 3)) (z (values x)))
(list x y z)))))
((Lambda
(x|3)
(call-with-values
(Lambda () (values 2 3))
(Lambda
(x|6|9 x|7|9)
((Lambda
(tmp|10|13)
((Lambda (z|17 y|17 x|17) (list x|17 y|17 z|17))
tmp|10|13
x|7|9
x|6|9))
(values x|3)))))
1)

--lars

Shriram Krishnamurthi

unread,
Dec 6, 1997, 3:00:00 AM12/6/97
to

For the reasons Lucier outlines, we provided a few syntactic
primitives in Mz/DrScheme:

define-values let-values letrec*-values (1)

let*-values letrec-values let let* letrec (2)

Of these, the constructs in the latter group are macros over those in
the former. The former are all constructs understood directly by the
compiler for efficient compilation.

One construct I haven't seen mentioned is begin0. Usually you can
define begin0 as a macro --

(begin0 e0 e1 ...) = (let ((v e0)) (begin e1 ... v))

-- but in the presence of multiple values, this is incorrect (assuming
that multiple values are not themselves a value). Thus begin0 is
retained as a primitive syntactic construct that is also handled by
the compiler. Likewise, our begin is designed to throw away multiple
values in the "for-effect" positions.

This is the real problem with multiple values. They are completely
pervasive, and (like working with multiple threads) it takes a certain
intellectual discipline to recognize all the places where they might
occur, and to account from them appropriately. The resulting
solutions will often lose in terms of efficiency.

A terrible hack. It's hard to estimate the number of times PLT has
considered being incompatible with R5RS because of mv's. But instead
here we are, wedded to them in a terribly abusive relationship.

'shriram

Albumen-Faced Petrofsky

unread,
Dec 6, 1997, 3:00:00 AM12/6/97
to

l...@everest.ccs.neu.edu (Lars Thomas Hansen) writes:
> >Defining let-values is pretty easy too, but, like multiple-value-call,
> >this does presumably-non-optimizable values-to-list translations:
> I belive the following definition does the right thing, and it does not
> require the intermediate list.

You're right. I gave up too easily. I retract my statement that
let-values is not efficiently implementable with syntax-rules and
call-with-values.

On a minor point, your version doesn't handle improper arg lists quite
right. Below are versions of let-values and letrec-values that handle
those and do no list creations. They've been tested against kawa 1.5.

> I think this conforms to the R4.95RS, but I'm not able to check that
> right now.

Neither can I, because I still have no idea where to get r4.95rs. Can
anyone help me?

-al

;; (multiple-value-bind (x y) (values 1 2) (+ x y)) => 3
(define-syntax multiple-value-bind
(syntax-rules ()

((multiple-value-bind vars form . body)
(call-with-values (lambda () form) (lambda vars . body)))))

;;(let ((x 1))
;; (let-values (((x y) (values 2 3)) (z (values x 4)))


;; (apply + (append (list x y) z)))) => 10
(define-syntax let-values
(syntax-rules ()

((let-values (binding ...) . body)
(let-values "bind" (binding ...) () . body))
((let-values "bind" () . letbody) (let . letbody))
((let-values "bind" ((vars val) . bindings) . letbody)
(let-values "bind1" vars val () bindings . letbody))
((let-values "bind1" (v1 . vrest) val (temp ...) bindings untemps . body)
(let-values "bind1" vrest val (temp ... x) bindings ((v1 x) . untemps)
. body))
((let-values "bind1" () val temps bindings . letbody)
(multiple-value-bind temps val (let-values "bind" bindings . letbody)))
((let-values "bind1" vrest val (temp ...) bindings untemps . body)
(multiple-value-bind (temp ... . x) val
(let-values "bind" bindings ((vrest x) . untemps) . body)))))

;; (letrec-values (((a b) (values 1 (lambda () a)))) (b)) => 1
(define-syntax letrec-values
(syntax-rules ()

((letrec-values () . body) (begin . body))
((letrec-values ((vars val) . bindings) . body)
(letrec-values "bind1" vars val () bindings () . body))
((letrec-values "bind1" (v1 . vrest) val (temp ...) bindings
untemps . body)
(let ((v1 'undefined))
(letrec-values "bind1" vrest val (temp ... x) bindings
((set! v1 x) . untemps) . body)))
((letrec-values "bind1" () val temps bindings untemps . body)
(letrec-values bindings (multiple-value-bind temps val . untemps) . body))
((letrec-values "bind1" vrest val (temp ...) bindings untemps . body)
(letrec-values bindings
(multiple-value-bind (temp ... . x) val
(set! vrest x) . untemps) . body))))

Shriram Krishnamurthi

unread,
Dec 6, 1997, 3:00:00 AM12/6/97
to

Albumen-Faced Petrofsky <alba...@wco.com> writes:

> You're right. I gave up too easily. I retract my statement that
> let-values is not efficiently implementable with syntax-rules and
> call-with-values.

Did you miss the heart of the macro? It expands thus:

((let-values "bind1" () val temps t bindings body1 body2 ...)
(call-with-values
(lambda () val)
(lambda temps
(let-values "bind" bindings t body1 body2 ...))))

What does (lambda temps ...) do? It takes all its arguments and
creates a _list_ out of them! So to avoid creating a first-class data
structure to hold the values, we have ... created a first-class data
structure to hold the values.

Be careful invoking arguments about the abilities of possibly mythical
compilers. In this instance, it may be pretty easy to turn them
around to argue against multiple values at all.

'shriram

PS: I have recently spent a lot of time looking at things related to
the CS AP exams. It strikes me that the debate about multiple
values in R5 eerily has a bit of the same feel as that over the
adoption of C++ as the AP's lingua franca.

PPS: Please first read Ashley and Dybvig's paper on multiple values
in Lisp and Functional Programming from 1994.

0 new messages