[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Issue: CONDITION-RESTARTS (Version 1)
- To: CL-Cleanup@SAIL.Stanford.EDU
- Subject: Issue: CONDITION-RESTARTS (Version 1)
- From: Kent M Pitman <KMP@STONY-BROOK.SCRC.Symbolics.COM>
- Date: Wed, 18 Jan 89 14:21 EST
Issue: CONDITION-RESTARTS
Forum: Cleanup
References: Common Lisp Condition System
Category: CHANGE
Edit history: 18-Jan-89, Version 1 by Pitman
Status: For Internal Discussion
Problem Description:
It was noted in the condition system document itself, and many people have
complained privately, that a major weakness of the condition system is the
inability to know whether a particular restart is associated with a
particular signalling action.
The problem being addressed shows itself in situations involving recursive
errors. The programmer wants to make sure that a restart obtained from
FIND-RESTART or COMPUTE-RESTARTS is in fact present for the purpose of
handling some particular error that he is actively focussed on, and not
for some other (outer) error which he was not actively trying to handle.
Proposal (CONDITION-RESTARTS:PERMIT-ASSOCIATION):
1. Define that it is an error for SIGNAL to be called on a condition
more than once.
2. Introduce a function COPY-CONDITION:
COPY-CONDITION condition [Function]
Returns a copy of the given condition.
3. Introduce a macro WITH-CONDITION-RESTARTS which can be used to
dynamically bind the association between a condition and a set
of restarts.
WITH-CONDITION-RESTARTS (condition-form restarts-form) &BODY forms
[Macro]
Evaluates CONDITION-FORM and RESTARTS-FORM, the results of which
should be a condition and a list of restarts, respectively. Then
evaluates the body of forms in implicit-progn style, returning the
last form. While in the dynamic context of the body, the function
COMPUTE-RESTARTS will, when given an argument that was the result
of evaluating the CONDITION-FORM, return the list of restarts that
was the result of evaluating the RESTARTS-FORM.
Only the innermost call to WITH-CONDITION-RESTARTS with a given
condition is relevant. In this way, the set of restarts associated
with a given condition can be dynamically extended or restricted.
Usually this macro is not used explicitly in code, since
SIGNAL-WITH-RESTARTS and ERROR-WITH-RESTARTS handle most of the
common cases in a way that is syntactically more concise.
4. Extend COMPUTE-RESTARTS, FIND-RESTART, ABORT, CONTINUE, USE-VALUE,
and STORE-VALUE to permit an optional condition object as an argument.
When the extra argument is not supplied, these functions behave
exactly as defined before. (Restarts are considered without
prejudice to whether they have been associated with conditions.)
When this argument is supplied, only restarts with the associated
with the given condition are considered. In all other respects, the
behavior is the same.
Passing a condition argument of NIL is treated the same as passing
no condition argument.
5. Add two new macros SIGNAL-WITH-RESTARTS and ERROR-WITH-RESTARTS:
SIGNAL-WITH-RESTARTS condition &rest restart-clauses [Macro]
This does several things:
1. It enters a context in which the indicated RESTART-CLAUSES
are available. They have the same form as the clauses in
a RESTART-CASE.
2. It evaluates CONDITION expression. [This is done after the
restarts are instantiated because the restarts are probably
still useful in the debugger if an error occurs during the
evaluation of the condition.] The result of the evaluation
must be a condition object.
3. It associates the condition which resulted from the evaluation
with the restarts established in step 1, using the equivalent
of WITH-CONDITION-RESTARTS.
4. It calls SIGNAL on the same condition.
ERROR-WITH-RESTARTS condition &rest restart-clauses [Macro]
Like SIGNAL-WITH-RESTARTS but uses ERROR rather than SIGNAL
in step 4.
6. Define that Common Lisp macros such as CHECK-TYPE, which are defined
to signal and to make restarts available, use the equivalent of
WITH-CONDITION-RESTARTS to associate the conditions they signal with
the defined restarts, so that users can make reliable tests not only
for the restarts being available, but also for them being available
for the right reasons.
Rationale:
1. The ability to recycle a condition object (including the ability to
resignal a condition) means that the same condition object might be
simultaneously active for two different purposes. In such a case,
no test (not even EQ) would suffice to determine whether a particular
restart belonged with a particular signalling action, since the
condition could not uniquely identify the signalling action. By saying
that a given condition may only be signalled once, we guarantee that
the condition can serve as a unique identifier for a signalling action.
2. Since there may now be some code which has begun to rely on the ability
to re-signal a condition, COPY-CONDITION will help to make this
transition easier. Instead of
(SIGNAL already-signalled-condition)
one can write:
(SIGNAL (COPY-CONDITION already-signalled-condition))
3. This is is the minimal level of support needed to set up an
association between restarts and conditions.
4. This provides a natural interface for retrieving and using the
information about the associations between conditions and restarts.
5. This provides a natural interface for the most common case of
wanting to signal a restart with some associated conditions.
Test Case:
(HANDLER-BIND ((ERROR #'(LAMBDA (C) (SIGNAL C)))) (SIGNAL "Test"))
was permissible, but this proposal makes it an error.
(DEFUN TEST-CONDITION-STUFF (OFFER-EXTRA-RESTART
USE-CONDITION-ARGUMENT
USE-FOUND-RESTART)
(HANDLER-BIND ((CONDITION
#'(LAMBDA (C)
(LET ((R0 (FIND-RESTART 'USE-VALUE))
(R1 (IF USE-CONDITION-ARGUMENT
(FIND-RESTART 'USE-VALUE C)
(FIND-RESTART 'USE-VALUE))))
(IF (AND R1 USE-FOUND-RESTART)
(INVOKE-RESTART R1 (EQ R0 R1))
(USE-VALUE (EQ R0 R1)))))))
(HANDLER-BIND ((CONDITION
#'(LAMBDA (C)
(USE-VALUE
(IF OFFER-EXTRA-RESTART
(WITH-RESTARTS
(SIGNAL (COPY-CONDITION C))
(USE-VALUE (X) (LIST 'EXTRA X)))
(SIGNAL (COPY-CONDITION C)))))))
(SIGNAL-WITH-RESTARTS (MAKE-CONDITION 'SIMPLE-CONDITION
:FORMAT-STRING "Test")
(USE-VALUE (X) X)))))
Previously, this was an error because it uses non-existent primitives, but
if you assume that
- COPY-CONDITION is implemented in the `obvious' way
- SIGNAL-WITH-RESTARTS just uses WITH-RESTARTS and SIGNAL
- FIND-RESTART ignores its last argument
in the obvious naive ways, it is possible to compare the old and new behavior:
Current Proposed
(TEST-CONDITION-STUFF NIL NIL NIL) => T T
(TEST-CONDITION-STUFF NIL NIL T) => T T
(TEST-CONDITION-STUFF NIL T NIL) => T T
(TEST-CONDITION-STUFF NIL T T) => T T
(TEST-CONDITION-STUFF T NIL NIL) => T (EXTRA T)
(TEST-CONDITION-STUFF T NIL T) => T (EXTRA T)
(TEST-CONDITION-STUFF T T NIL) => T (EXTRA NIL)
(TEST-CONDITION-STUFF T T T) => T NIL
Current Practice:
Presumably no implementation does this yet.
Cost to Implementors:
Several small, relatively modular changes.
Cost to Users:
Except for the change to the recyclability of restarts, this change is
upward compatible.
Probably very few if any users currently take advantage of recycling
restarts, so the cost to users of this change is very slight.
Even in the case where recycling is used, a straightforward rewrite in
terms of COPY-CONDITION is probably feasible.
Cost of Non-Adoption:
Use of restarts would not be nearly as reliable.
Benefits:
It would be possible to write code which was considerably more robust.
Aesthetics:
Some people might consider this proposal to make things slightly better
because it avoids some ambiguities. Others might consider it to make
things slightly worse because it adds additional complexity.
Discussion:
Pitman thinks a change of this sort is important.