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

Re: exception systems




   Date: Thu, 18 Apr 1996 20:41:47 +0000
   From: William D Clinger <will@ccs.neu.edu>

   The first question is whether I understand your proposal
   well enough to write a handler for your add-one example.

   [examples elided]

These look fine to me, except that, as you ask about
below, FIND-RESTARTS is guarenteed to return only restarts.

   Is it guaranteed that every element of the list returned by
   FIND-RESTARTS is a restart, so the handler doesn't have to check?

Yes.

   Why does FIND-RESTARTS return a list?

Because a handler interested in interactive restarts
is going to want to get all of them.

   Is the list in any particular order?

No.

   Is there any way at all for the handler to
   decide which element of the list to use?

For those restarts that have interactive procedures you can
ask the user.  Other than that there really is no way to
choose.  I believe, and Chris Hanson seems to agree, that
most restarts are invoked interactively.  For non-interactive
use restarts should really have fields like conditions, which
seems overly complex.

   In my experience, the two most common actions that can be
   taken by an exception handler are

     1.  To retry an operation with different arguments.
     2.  To return a result to the continuation of an operation,
	 bypassing the operation altogether.

   Many exception handlers need to be capable of either action.
   I don't see how the proposed system allows this choice unless
   I resort to an ugly kluge such as

   [ugly kluge elided]

Unless I am misunderstanding what you are asking, the two
choices could be offered as separate restarts.  With appropriate
restart tags the choice between them could be easily be made
by a handler.

(define (add-one x)
  (if (integer? x)
      (+ x 1)
      (call-with-restart
	<restart-tag>
	<condition-predicate>
	add-one                       ; retry
	(lambda (out) (display "provide argument" out))
        (lambda (in out) ...)
        (lambda ()
          (call-with-restart
	    <restart-tag>
   	    <condition-predicate>
            (lambda (x) x)            ; proceed
	    (lambda (out) (display "provide result" out))
            (lambda (in out) ...)
            (lambda ()
	      (signal (make-wrong-type-argument-condition
		       add-one
		       x))))))))

There needs to be a macro for installing multiple restarts.

   It appears that a restart is typically selected by indexing
   in five distinct dimensions:

     1.  the space of handlers (by daisy-chaining)
     2.  the space of restart-tags
     3.  the space of condition classes (as defined by the
	 predicates)
     4.  the space of natural numbers (giving the position
	 within a list of restarts)
     5.  the space of condition data (the stuff stored in the
	 slots of a condition)

I don't fully agree with number five.  Restarts are not
associated with particular conditions.  It is true that a
handler that is examing restarts got control via some
condition, but there is no more necessary connection
between the restart and that condition than between the
restart and the thunk whose execution raised the condition,
for example.

The other four seem inevitable to me.  Number four could
be finessed by replacing FIND-RESTARTS with:

  (find-interactive-restarts) -> list of restarts
  (find-restart restart-tag) -> restart or #f

If more than one restart that matches <restart-tag> is
in place, FIND-RESTART could either signal an error, or
return the innermost or outermost.

   There are several things I don't understand about this
   indexing.

   I assume WITH-HANDLER places its first argument at the head
   of the handler chain for the dynamic extent of the call to
   WITH-HANDLER.  Is this correct?

Yes.

   I would guess that CALL-WITH-RESTART associates its first
   argument with all but the last argument for the dynamic
   extent of the call to CALL-WITH-RESTART.  Is this correct?

Yes.

   The process of fetching a restart strikes me as excessively
   complicated, unnecessarily inefficient, and unsafe.

   [illustrative example etc. elided]

Errors within handlers are a problem as they are likely to
lead to infinite loops.  Perhaps the handler in place whan
a handler is invoked should be that of the WITH-HANDLER call
and not that in place when the condition was signalled.

   In other words, you can't install a new handler without using
   a mechanism that effectively allows you to throw out all the
   existing handlers and start over from scratch.  It would be
   safer to provide a mechanism that allows you to install a
   handler that will see only the conditions it is designed to
   handle.  This could also be more efficient, as it could
   eliminate two, three, or maybe even four dimensions of indexing.

I agree, especially about the lack of safety.  As a first step
WITH-HANDLER could take a condition type as an additional argument
and only invoke the handler for conditions that match the type.
Presumably this is what almost all handlers will be doing anyway.

 (with-handler condition-type handler thunk)

Believe it or not, I had dropped the condition-type from
WITH-HANDLER to avoid complaints about complexity or lack of
generality.  Your safety argument is a convicing one.  Thanks
to multiple inheritance, only one condition-type argument
is needed :).
                                    -Richard