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

Issue: CONDITION-RESTARTS (Version 1)



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.