[r6rs-discuss] Re: [Formal] Recursive exception handling considered harmful

From: John Cowan <cowan>
Date: Sat Mar 24 14:01:47 2007

----- Forwarded message from Taylor R Campbell <campbell_at_mumble.net> -----

Date: Wed, 21 Mar 2007 18:14:39 +0000
From: Taylor R Campbell <campbell_at_mumble.net>

   Date: Wed, 21 Mar 2007 10:32:02 +0100
   From: Michael Sperber <sperber_at_informatik.uni-tuebingen.de>

> - The signaller can ensure that, even if all handlers decline to
> handle the condition, some action will be taken as a last resort;
> see, for instance, the example above of signalling warnings, which
> we want to report somewhere even if all handlers decline to handle
> the warning.

   Correct if I'm wrong, but isn't this precisely the job of a potential
   future restart system?

No. This has to do with ensuring that information is not lost even in
broken situations where there is working condition handler available
to print the message in a nice way.

                           (I also looked into the issue of adding a
   restart system later, and I see nothing in R5.92RS that would prevent
   it or would make it unnecessarily difficult. Maybe you could be more
   specific.)

It's not that I think it's difficult; rather, I don't expect to see it
in R6RS, and I'm not prepared to put forth any more of my effort on
something in R6RS that is as unlikely to happen as the module system I
want, the Unicode support I want, the modular arithmetic I want, and
so on.

               I'm bothered by the fact that your chain of handlers
   passes control upwards until the very end, and then passes control
   *back in*. The discontinuity isn't bad in itself, but it seems
   strange that it's done implicitly rather than explicitly. The
   discontinuity is, as I understand it, exactly what distinguishes the
   exception system from the restart system in Common Lisp.

Control is passed back in *only* as a last resort, if no condition
handler handled the condition, and if the signaller supplied an action
to be taken as that last resort. If the signaller supplied no action,
then I think either that serious conditions should make the signalling
fatal (e.g., by aborting the thread) and non-serious conditions should
just return from the signalling, or the that effect should be
unspecified provided that control never returns to the signaller.

Also, control is *never* returned to the signaller implicitly (except
perhaps for non-serious conditions if all handlers declined and no
default action was supplied), not even by a condition handler's
return. *Only* by explicit action of invoking an escape procedure
(for instance, stored in an object describing a recovery path
associated with the condition, or what Common Lisp and Dylan call
restarts) can any condition handler ever return control to the
signaller, and that is only if the signaller explicitly provided such
an escape procedure.

   Dealing with warning messages is easily accomplished by *not*
   declining and instead handling by printing and returning to the call
   to `raise-continuable'. The specific means of printing is more likely
   to be established further out than at the point of the raise.

That's right, in the preferred case. But in the screw case where
there is no condition handler available, for the sake of robustness we
really don't want to lose any information.

> - The condition signaller always has the power to decide whether
> control may return to it; no condition handler may disagree, except
> by semantics-violating features in a debugger. The default is that
> control may not return to the signaller, which is what most programs
> expect.

   This seems to exactly describe what's in R5.92RS.

That is true, but only in the presence of RAISE-CONTINUABLE, which I
think is a bad idea because it tries to fill the space of a recovery
protocol without actually being one. *Any* handler can decide to
recover by simply returning, which may be a mistake that only further
screws up the program. This may hang the system:

  (WITH-CONDITION-HANDLER (LAMBDA (C) 'FOO) (LAMBDA () ...)),

because we could get a recursive error that indefinitely repeats
looping back to the handler, if the signaller used RAISE-CONTINUABLE
and expected a valid datum to be returned. It's somewhat harder to do
with a principled recovery protocol, because we'd have to go to the
trouble of requesting to retry the computation with a particular
substitute value, and it would be much harder to bring about
accidentally.

> - Only a constant amount of stack space separates the handler from the
> signaller. This helps to avoid potential stack space shortage
> issues, and it also helps to remove clutter from stack traces, which
> are almost always computed from the continuation of a condition
> handler (a debugger, again, being the most common example of a
> condition handler).

   However, the stack trace might be useful.

I'm confused about what you're rebutting here. I didn't say that the
stack trace wasn't useful; it's unnecessary *clutter* in the stack
trace, caused by a chain of condition handler invocations (each of
which involves the establishment of dynamic state), that is not
useful.
                                              Also, "stack space
   shortage" is not really a common problem in Scheme implementations, at
   least not one where the exception system (i.e. the R5.92RS exception
   system) is a good place to deal with it. (This, IMHO, is the big
   deficiency with the CL system: It tries to deal with everything using
   the same mechanism.)

I didn't say that the condition system should deal with stack space
shortage. I said that the condition system shouldn't *contribute* to
it unnecessarily.



----- End forwarded message -----

-- 
John Cowan  cowan_at_ccil.org   http://ccil.org/~cowan
It's the old, old story.  Droid meets droid.  Droid becomes chameleon.
Droid loses chameleon, chameleon becomes blob, droid gets blob back
again.  It's a classic tale.  --Kryten, Red Dwarf
Received on Sat Mar 24 2007 - 14:01:41 UTC

This archive was generated by hypermail 2.3.0 : Wed Oct 23 2024 - 09:15:01 UTC