[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Multiple Values: An Opinion



    I'm sorry, I know I'm not supposed to be on this line, but I just had
    to state my opinion of multiple values. I think this is one (!) of the
    most poorly thought out (and most haphazardly integrated) concepts in 
    common lisp. I'd hate to see scheme suffer from the same disease.

I can understand your objections when you look at the CL book and
examine the rules for when multiple values are passed back and not.
On closer inspection, however, you will notice that the rule is
uniform (although specified in utmost detail): When dealing with a
compound expression, multiple values are passed back from the
subexpression into which the evaluation of the compound expression
reduces.  In other words, tail-recursion is what determines when
multiple values are passed back.  Unfortunately CL does not require
its implementations to be properly tail-recursive, so they could not
explain the semantics of multiple values in terms of tail-recursion.
We don't have this problem, so the behaviour can be described simply,
and the huge case list can be added as examples, not as the definition
of the bahaviour.

Agreed that there is no semantic need for multiple values, and that
the main consideration is efficiency, but there is no reason not to
add them to the language with inessential status, as long as we also
specify that implementations are free to make them be the obvious
procedures which work on lists, or even the suggestion below (which I
prefer).

I don't agree with you on one count however.  Most of the compiler
optimizations that I see proposed very often suffer from a few bad bugs:

- The idea is simple, so it is assumed that the implementation is also
simple.  This is obviously a fallacy.  I'm specially wary of
optimizations that require a great deal of analysis.  I'm not saying
it cannot be done, just that it is often the case that it takes a
great deal more work than anticipated.

- The optimizations break down across module boundaries.  The main
problem with static analysis is that it needs a closed world model.
This is often hard to provide, and even inappropriate in an
interactive development environment.

- Static optimizations to improve performance badly hurt interpreters.
I know the MIT crowd is in the minority here, but we believe in
interpreters as our main development tool.  Although we also want high
performance compilers, we don't want to sacrifice any performance in
the interpreter.

Consider the following proposal instead, which does not require much
(if any) compiler overhead:

(define (receive-values fn th)
  ((th) fn))

(define (values . all)
  (lambda (receiver)
    (apply receiver all)))

Note that a compiler which understands a little about tail arguments
can optimize the above relatively easily, given that the construction
and destructuring of the list is purely local and contained inside
VALUES.  Even further, since the whole mechanism is contained in 2
procedures, they can easily be implemented as primitives, with
whatever efficiency is desired.  Static compiler analysis to reduce
consing can still be used, but this time it is closure analysis of the
sort many Scheme compilers already do.

Note that the above proposal implies that intermediate forms in AND,
OR, and possibly other special forms would have to be treated
specially, since there is a difference between returning a "normal"
value, and explicitely returning 1 value.

PS: There are two other objections to your message, both minor:

- SET! is a the keyword of a special form, thus it cannot be mapped.

- I (and a fair amount of other people) do not believe in cdr-coding.
It is clearly expensive on stock hardware, and it is not clear to me
that it is not expensive on special purpose hardware, besides adding
unnecessary hair to the complete system and in particular the garbage
collector, and we all know where this leads.