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

No Subject



Cc: halstead@crl.dec.com, rhh@lcs.mit.edu
Subject: multiple return values -- APPLY-OPTIONAL, a modest proposal
Date: Wed, 23 Aug 89 15:18:27 EDT
From: halstead@crl.dec.com

Here's a fresh look at the problem.  It strikes me as kind of way-out,
but maybe not too way-out for serious consideration.

We are concerned with having continuations that may accept multiple
arguments (or no arguments!) and having some way to return to a
continuation the number of arguments that it wants.  In the simple
case (no ACCEPTS?, exact arity match required), we can write VALUES
as a procedure

	(define (values . vals)
	  (call/cc
	    (lambda (k) (apply k vals))))

Yes?  (The above is intended to be non-controversial.)

The problem arises when we want to add flexibility to the interface
between VALUES and the continuation, e.g. to implement the Common Lisp
"feature" that allows excess values to be discarded if they are not
wanted by the continuation.  This is the motivation for having ACCEPTS?.

Let's step back and consider another case where Scheme already allows
for flexibility in the number of arguments passed.  We can already
write a procedure like (lambda (a b . rest) ...) that requires 2 or more
arguments, so the callee can express a willingness to be flexible in
the number of arguments passed, within some limits.  The proposal to
include ACCEPTS? is motivated by a desire to allow the *caller* some
similar flexibility, by letting it effectively negotiate with the callee
to settle on a number of arguments that the callee will be happy with.
The problem with that, as Kent points out, is that the negotiation is
only skin-deep, and can't take into account restrictions built into
the callee's definition, e.g., in

	(lambda (a b . rest)
	  (if (> (length rest) 2)
	      (error ...)
	      (let ((c (car rest))
		    (d (cadr rest)))
		.... c .... d ....)))

where the procedure really only "accepts" either 2, 3, or 4 arguments.

Can we solve this problem by pushing it down a level?  Here's a 3/4-baked
proposal to do just that:  introduce a new procedure APPLY-OPTIONAL that
is used as (APPLY-OPTIONAL f a1 a2 ... an optional-args).  If f cannot
accept at least n arguments, then this "is an" ("signals an"?) error.
If f requires at least m>=n arguments, then m-n additional arguments
are taken off the list of optional-args.  When f looks like it can
accept an unbounded number of arguments, I think you have to pass all of
optional-args.  (Perhaps this is the Achilles heel of the proposal.)
Now we can write a VALUES that operates as in Common Lisp like this:

	(define (values . vals)
	  (call/cc
	    (lambda (k)
	      (apply-optional k vals))))

(or you can vary it to suit your tastes regarding special treatment of
supplying 0 arguments to VALUES, etc.).

I like this idea because it makes the call interface more symmetrical
regarding optional arguments:  if the callee gets to specify flexibility
in accepting calls from different kinds of callers, why shouldn't the
callers be able to specify similar flexibility in the callees they will
deal with?  I also think this proposal avoids the problems that Kent
has criticized ACCEPTS? for:  in this proposal, the code inside the
procedure gets to decide whether it will accept a given set of arguments
-- or it can pass the buck on that decision -- without needing to encode
its criteria in a form that ACCEPTS? can decipher.  On the other hand,
some cans of worms are opened:  my 2-, 3-, or 4-argument procedure above
would reject a call like (APPLY-OPTIONAL F 1 2 3 '(4 5 6)) even though
there is a way to satisfy F's requirements.  To fix this, we need to give
a procedure some hooks so it can tell which of its arguments were optionally
supplied.  Urk!

Comments?						-Bert Halstead