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

a (revised) draft of R5RS



   Date: Tue, 18 Mar 1997 17:36:21 -0500
   From: Richard Kelsey <kelsey@research.nj.nec.com>


   ... The new draft (R4.96RS) is available ...

       - The classification of features as essential or inessential has
	 been removed.  There are now three classes of built-in procedures:
	 primitive, library, and optional.  The optional procedures are LOAD,
	 WITH-INPUT-FROM-FILE, WITH-OUTPUT-TO-FILE, TRANSCRIPT-ON,
	 TRANSCRIPT-OFF, and INTERACTION-ENVIRONMENT, along with the
	 more-than-two-argument versions of - and /.  None of these are in
	 the IEEE standard.

Not sure if this comment is "in order" but I'll risk it anyway.  It's been
bugging me a long time and there never seems a "right time".  The comment
is not "simply editorial", but I was reminded of it by the items in the
list above, which I have long believed are a semantic embarrassment because
of the bad way that dynamic extents and escape procedures interact.

I'd like to see WITH-xxx-xxx-FILE functions changed to pin down the effect
of escape procedures.  Is there a looming meeting for doing this?  Are we
equipped to engage in discussion on this issue?

It bothers me that because we have neglected UNWIND-PROTECT as a basic
operator (whether functional or special form, it doesn't matter to me,
I just care about the interaction with call/cc), we have to leave pretty
important stuff undefined.

The issue seems simply this:  when you exit the park, I think you should 
have to get your hand stamped if you want to be re-admitted later.  (Sorry,
I just got back from vacation and theme parks, so this seems the best
analogy.)  Full continuations have the problem that going to the movies at
a theater has--if everyone leaves in disgust, you can't stop the movie because
your default assumption is that they will all be back, their tickets having
been torn upon entry and sufficing to allow re-entry.  But theme parks 
recognize this problem and they make you distinguish on exit about your
intent--denying you readmission if you don't say specifically you want 
back in.  Well, I want the same for escape procedures.

There are two ways to do this, and I'm not fussy about which to use, though
I have a preference for the second of these; note: i'm ignoring a possible
interaction with multiple values because I've not had a chance to review what
got worked out on that, but I don't believe any conflict this raises will
be unresolvable with multiple values.  (Consider my explanation and proposed
syntax to be simplified for the all continuations and return values are
single-value/argument case.)

PLAN A: Add an argument to continuations

 (call/cc2a (lambda (k) ...))

where k is a function of two arguments--return-value and final?.  The final?
says whether you want a hand-stamp so you can call the continuation a second
time; the penalty for getting a stamp and not using it is that only the
garbage collector will run any pending cleanups for the context.  If instead
you call the escape function for the last time (final?=#t), then all cleanups
from the point of call back to the point of issuance get run, and all escape
functions issued in that interim are defined to be invalid.

call/cc could be offered as optional compatibility with old things and
would default to a final? of #f, preserving the right to re-enter;
But call/cc2a would be the real primitive.

Some might prefer to add syntactic or functional sugar, such as:

 (define (escape-temporarily where value) (where value #t))
 (define (escape-forever where value) (where value #f))

to hide the boolean and express the intent in another way, but all of these
variations are essentially the same in my mind.

PLAN B: Add an argument to call/cc

 (call/cc2b (lambda (k) ...) reusable?)

where reusable? says whether or not the escape procedure k can be invoked
more than once.  This binds one earlier to the proposition that the k was
not possible to exit multiply, but makes it easier to tell when "cheap" 
compilation of a catch is possible because a conventional stack model was
being intentionally observed.

I don't like this version quite as well because it almost wants to be
 (call/cc2c (lambda (k) ...)
   use-of-k-permits-k-to-be-marked-invalid?
   normal-exit-permits-k-to-be-marked-invalid?)
That is, the question of whether k is marked invalid might be different 
depending on whether exit is normal or not.  That is, one might claim that
an exit without using k preserves the right to use k even if he's only a
one-time-use continuation; but even though I think someone might confuse 
themselves into believing so, I think that's a bad plan because the most
common use I can expect to want to be put to resuable? in the call/cc2b
case above is to say "use a regular stack" and if normal return can cause
a need to leave open the option of re-entry, I've not achieved that.

~~~~~

So, all in all, I'd rather PLAN A.

But what I really want is for use of continuations to say explicitly somehow
whether you're meaning to use the full power or not, because I think the 
failure to do so (a) inhibits important optimization and (b) makes
unwind-protect a mess.

I'd like to be able to define with-open-input-file by saying that it
does something like this [I have no implementation to test this in, so I hope
I didn't make any coding errors that confuse it--I'm just sketching here]:

  (call/cc2a (lambda (k) 
	       (let ((port #f))
		 (unwind-protect 
		   (lambda () (set! port (open filename))) ;wind action
		   (lambda () (if port (close port))) ;unwind action
		   (lambda (body-thunk) ;protected action
		     (k (body-thunk port) #f)) ;<--if it exits, that's it
		 ))))

Moreover, one could then explain in precise terminology that if someone
implemented a with-open-input-file that does 
 (k (body-thunk port) #t) ;<-- plans to re-enter
then what they are saying is that the CLOSE operation on the port will
not happen until k is about to be gc'd, since that's going to be the
first time the system can prove that a final exit has occurred.

Having your unwind-protect cleanups run at a more intuitive time than GC
is the carrot that makes one want to provide #t as a final? argument
to an escape procedure.  Those who know they will be back and will later
pass #t will not find a problem with waiting to GC to run unwinds.  The only
one hurt will be the one who is going to leave and isn't sure if he'll be 
back--and such a person deserves to bear the cost of their indecision.  Right
now, the indecisive aren't who bear the burden--it's everyone who has to 
suffer the footnote in with-open-input-file, etc. where it says the effect
of escape procedures is implementation-dependent.