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

Error Proposal #5: difficulties and suggestions



I've just finished an implementation of the error system and would like
to share some of the things I ran across that pose a problem.

First, I believe there should be a way to set default handlers, report
methods and default tests for proceed cases other than redefining the
object in question. This isn't a big deal, but it does provide the user
some flexibility. This can cause problems if you're depending on default
handlers to ensure that SERIOUS-CONDITION ends up in the debugger if it
isn't handled, but I'd rather handle that directly in the code. That is,
ERROR always calls debug after signalling the condition and SIGNAL calls
debug after signalling if the condition is of type SERIOUS-CONDITION. 

The rest of the questions have to do with proceed cases. First of all,
for a given PROCEED-CASE form must the selectors be unique? I propose
that they not be. I can imagine writing a PROCEED-CASE in which
different arms with the same selector were enabled at different times by
their :TEST methods. Allowing this only adds a small complication to the
implementation of PROCEED-CASE: instead of using the proceed case name
as the selector, you need to generate a value that is carried along with
the PROCEED-CASE object that represents that arm. An integer suffices
for this.

Proceed functions appear to serve two purposes: they are a repository
for default information for that proceed type (test, report,
parameter-gathering), and they provide a simple way to find and invoke
proceed cases. These two functions are somewhat at odds with one another
when you are invoking a proceed case from an interactive debugger. In
this situation, the user has picked a particular proceed case that
should be invoked, so the obvious thing to do is call
INVOKE-PROCEED-CASE. Unfortunately, this will not use the
parameter-gathering mechanism specified in the proceed function. If
instead you see if there is a proceed function defined for that proceed
case and invoke it to get the parameter-gathering done, the proceed
function can end up invoking the wrong proceed case if there are others
with the same name that are more recently bound.

To illustrate, consider the following (somewhat contrived) code:

(define-proceed-function use-value
  (v (eval (prompt-and-read "Enter expression to be EVALed: ")
)))

(block outer
  (condition-bind
    ((foo-error
     #'(lambda (condition)
         (proceed-case (error 'foo-error)
           (use-value (ignore x) :test true
             :report "Return to inner proceed-case"
             (return-from outer (values x 'inner)))))))
    (proceed-case (error 'foo-error) 
      (use-value (ignore x) :test true
        :report "Return to outer proceed-case"
        (values x 'outer)))
  )
)

Evaluating this will land you in the debugger with FOO-ERROR. You might
see something like:

	Error FOO-ERROR.
	1 - Return to inner proceed case
	2 - Return to outer proceed case
	Selection: 

Now, the user picks selection 2. If the debugger implements proceeding
by simply invoking the given proceed case, the final values are NIL
OUTER. If the debugger instead invokes the proceed function, the user
will be prompted for a new value, say 7, and the final values will be 7
INNER. Not quite what the user asked for.

To fix this problem I propose giving proceed functions another implicit
parameter, PROCEED-CASE. If this is NIL then the proceed function
behaves as it does now: it finds the most recently bound proceed case
with that name and invokes it. If a proceed-case is supplied, it will
invoke that one specifically if it is currently enabled. It is an error
to provide a proceed case whose name differs from the proceed function.
If the proceed case is not enabled, the proceed function simply returns
NIL.

		-- Andy. --