From: David Mittman
Subject: handler-bind vs. handler-case
Date: 
Message-ID: <mittman-0512002129540001@adsl-64-162-178-246.dsl.lsan03.pacbell.net>
I'm puzzling over the non-syntactic differences between handler-bind
and handler-case, especially with regards to the unwinding of the stack.
Can anyone provide a description of the non-trivial differences?
When, if one can speak generally, should I be concerned with using one
over the other?
From: Kent M Pitman
Subject: Re: handler-bind vs. handler-case
Date: 
Message-ID: <sfwg0k2ypzz.fsf@world.std.com>
·······@jpl.nasa.gov (David Mittman) writes:

> I'm puzzling over the non-syntactic differences between handler-bind
> and handler-case, especially with regards to the unwinding of the stack.
> Can anyone provide a description of the non-trivial differences?

Sure.

> When, if one can speak generally, should I be concerned with using one
> over the other?

The primary difference, as illustrated in the Notes section for
HANDLER-CASE in CLHS, is that HANDLER-BIND is subprimitive to
HANDLER-CASE.

In particular, HANDLER-BIND takes control *before* any transfer of control
occurs, while HANDLER-CASE doesn't let you run any user-code until after
you've already unwound.  That is, when you do:

  (HANDLER-CASE (FOO)
    (ERROR (C) (BAR)))

the (BAR) is going to run *after* unwinding from however deep into
(FOO) you were when the error occurred.

By contrast, HANDLER-BIND takes control *inside* the call to SIGNAL and
is capable of accessing restarts that are dynamically between the call 
to SIGNAL and the call to HANDLER-BIND.  So, for example,

 (WITH-SIMPLE-RESTART (FOO "Outer foo.")
   (HANDLER-CASE (WITH-SIMPLE-RESTART (FOO "Inner foo.")
                   (ERROR "Hello."))
       (ERROR (C) (INVOKE-RESTART 'FOO))))

will select the outer FOO restart (though the outcome will be pretty
much the same because I kept the example small.)  Whereas,

 (WITH-SIMPLE-RESTART (FOO "Outer foo.")
   (HANDLER-BIND ((ERROR #'(LAMBDA (C) (INVOKE-RESTART 'FOO))))
     (WITH-SIMPLE-RESTART (FOO "Inner foo.")
       (ERROR "Hello."))))

will invoke the inner FOO restart.  This is important because "serious"
error handling will want to be done with HANDLER-BIND where all the
restarts are available.  e.g., if one does:

 (HANDLER-CASE (FOO)
   (ERROR (C) (LET ((R (FIND-RESTART 'CONTINUE C))) 
                (IF R (INVOKE-RESTART R)))))

then one is going to lose because anything signaled within FOO will
almost surely not have any CONTINUE restart still active by the time
you've returned this far out.  The call chain is likely to look like

 (DEFUN MY-SYMBOL-VALUE (NAME)
   (COND ((BOUNDP NAME) (SYMBOL-VALUE NAME))
         (T
          (RESTART-CASE (ERROR 'UNBOUND-VARIABLE ...)
            (USE-VALUE ...)
            (STORE-VALUE ...)))))

and the error is going to be in MY-SYMBOL-VALUE with all the restarts
there, so any call to

 (HANDLER-CASE (MY-SYMBOL-VALUE ...) ;or something calling MY-SYMBOL-VALUE
   ...)

is going to throw past all the cool restarts that are possible and out
to the place where probably only Abort restarts and other generic junk
like that is left.

There is a similar issue with RESTART-BIND.  RESTART-CASE actually transfers
control first to the restart (eliminating all the other options as it does)
whereas RESTART-BIND, because it doesn't unroll the state, can take a 
corrective action (such as to expunge a directory when you get a 
file-space-exhausted kind of error and you select "expunge and retry")
and ALSO can throw to some other restart (such as some retry option) which
will still exist because the selection of one restart hasn't erased the
presence of the others as it would in RESTART-CASE.  I could provide examples
though they are similar to what I've done above and I'm already running long
here.  See also the notes for RESTART-CASE in CLHS, which show the relationship
of the two operators "clearly" (depending on your notion of clarity, I 
suppose).

Let me know if this leaves open questions.