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

Re: Multiple values in Dylan

I said:
    I think [multiple values] should be defined to be, in effect, 
    the arguments to the current continuation: no more, no less. 
You asked:
    Could you expand on your above suggestion a bit, perhaps with some examples
    of what common cases would look like?  Not being a Schemer, I'm not sure I
    understand quite what you mean by "arguments to the current continuation"
    and what this would imply in practice.

I'm sure there's nothing new in this explanation for you and most 
other readers of this mailing list, but I'll elaborate anyway.

Informally, the current continuation is "what happens next", or more
commonly, "what happens when we return from this function call".  I
said "in effect" because Dylan has no explicit representation of the
current continuation, although Lisp compilers have used
continuation-passing style at least since RABBIT (1978).  Scheme lets
you get at it directly, as a first-class object.  Common Lisp has
multiple-value-call, which lets you specify the continuation function.

When a function returns a single value, it has the effect of calling 
the current continuation with that value.  So when we're evaluating 
(+ (- a b) c), we can say that after we've computed the difference 
of a and b, we could pass that value to the current continuation, 
which is something like (lambda (x) (+ x c)).

My point is that (values x y) is simply a notation for calling 
the current continuation with two values instead of one. For example,
in (bind ((quotient remainder (floor a))) ...), once we're inside 
the call to floor, the current continuation is 
(lambda (quotient remainder) ...).  Clearly, that's a function of 
two arguments; they can have types, and we should be able to use #rest
and #key and all the other lambda-binding machinery.

So in this view, there's no such thing as "returning some values".
There's only "calling the continuation with some values", and whatever
rules Dylan uses for passing arguments to functions (e.g., it doesn't
automatically ignore extra parameters or supply #f for missing
parameters) it should use for multiple values: "no more, no less".

My general belief is that Lisp has suffered both internally and
externally because of such sloppiness: "floor returns two values, but
you can ignore the second one if you want, and if you ask for more
than two values, you'll get some #f's thrown in for free." Internally,
it leads to error-prone code, and it prevents the compiler from
catching a certain class of errors.  Externally, people from "tighter"
languages (pick your favorite) see this, and it merely reaffirms their
belief that Lisp is, was, and forever shall be inefficient, not
commercially viable, no competition for "real" languages like C, etc.,
etc., etc.

I don't think the language need be completely inflexible, of course.
It's very handy to be able, for instance, to add reals and integers
without explicitly having to coerce the integer into a real, and I
think most people would agree that it's worth the price we pay for
that.  But I've never heard a convincing argument in favor of the
sloppy binding for multiple values, or, for that matter, for NOT
declaring types of variables.  [In a recent comment on the Lisp FAQ, I
took exception to the recommendation that we shouldn't "add
declarations to code until it is fully debugged".  That's the sort of
attitude, in my opinion, that's gets us into a lot of trouble.]

Of course, there's a cost to being hard-nosed.  If we follow my
suggestion, you won't be able to write (bind ((q (floor a))) ...), or
even (+ (floor a) b), since the wrong number of values are being
"returned".  Of course, this one is easy to fix, by defining a
function ("floor1") that returns only the first value, just as MODULO
returns only the second value.

To do this "right", I'd also want to allow BIND to use #key (and maybe
even #next), thus getting rid of the distinction between the
variable-binding protocols of BIND and METHOD.  Indeed, I believe BIND
is the only way in Dylan to get effect of multiple-value-call; if BIND
can't use #key, then the only way to use BIND call a method that takes
keywords is to use #rest and APPLY; what a waste!