ulr
...@mips.complang.tuwien.ac.at (Ulrich Neumerkel) writes:
> Now an error is signaled in the protected-form and (part of) the state
> for cleanup-form is corrupted (related to the signalled error).
> The cleanup-form thus reports its own seemingly unrelated error.
No. The original error has already been handled by this point.
Lisp's condition system is much more powerful and sensible than the
C++/Java/Python/etc. try/catch exception handling system. Let's examine
that system for a bit before I explain why Lisp is different. If you're
very unlucky, I'll also explain why Java's exceptions are quite so
godawful slow. (I'll pick on Java here because it's an easy target.
Err..., no, because it's fairly well known and representative of a
number of languages with similar exception handling facilities -- it's
better known than Python and it's much simpler than C++. But it is also
an easy target -- and GLS should have known better.)
So: an error occurs in our Java program: an exception is constructed (if
necessary) and thrown. The runtime system takes over and looks at the
call stack. It unwinds the stack until it finds an active `try' block.
It looks to see whether the `try' block has attached a `catch' clause
which matches the exception, or a `finally' clause. If the latter, it
executes the stuff in the `finally' block, and continues the search[1]. If
the former, it executes the matching handler. In both of these cases,
the `catch' or `finally' code is executed in the same dynamic context as
the immediately surrounding stuff -- any functions called within the
`try' are dead and gone. They can't come back.
The most important point to notice here is that, in a `finally' block,
the exception hasn't yet been caught. When the `finally' code finishes,
we'll resume the search for a matching handler.
Right. Lisp is different. (Take that out of context and use it as a
slogan.) Lisp, at least in this regard, is /better/. When a condition
is signalled (ooh, new words -- think `exception is thrown' if it makes
you feel happier, though the concepts are subtly different), the SIGNAL
(or ERROR, or whatever) function looks at a table of active handlers,
and picks the most recent handler which matches, and invokes it. There
and then. No unwinding, nothing. Not even a hint of UNWIND-PROTECT.
The handler can do all sorts of things now. It can return, for
instance, in which case the system looks for another handler; if we run
out of handlers, the program might continue merrily on its away,
blithely assuming that everything has been fixed. Or the handler might
make a nonlocal jump -- GO, RETURN, THROW, or INVOKE-RESTART -- in which
case we start unwinding stack frames and doing UNWIND-PROTECT stuff.
But the critical difference here is that the condition handlers have
/already/ had their chance to do something about the condition, and
we're just finding a safe place to continue execution.
(HANDLER-CASE does do unwinding before it invokes your handler; but
if you cared, you'd use HANDLER-BIND instead, right?)
So, why do Java's exceptions suck? It's because Java exception objects
(java.lang.Throwable things) carry a stack backtrace with them, which
exception handlers might look at and do things with. But by the time
the exception handler's been called, some of the stack frames have
already been discarded. Therefore the runtime must reify the stack in
advance (or at least build the backtrace while it's unwinding the
stack).
Lisp doesn't need to do this. If you register a condition handler using
HANDLER-BIND, it gets called in the context of the whatever signalled
the condition -- so the stack is still there for poking, prodding,
fiddling with or printing. Or debugging, of course.
It's a crying shame that other languages haven't adopted Lisp's clear
separation between handling conditions and resuming execution.
[1] Java discards its exception if a `finally' block `ends abruptly' --
e.g., returns, or throws an exception. This really will lose
exceptions. Sorry: Java blows goats.
> In a programming situation the debuggerhook break - going into the
> first signaled error might be a good reaction. But offline, with
> some toplevel that briefly logs the error and recovers, priorities
> are differnt.
But that's not a problem. The toplevel HANDLER-BINDs its handler; the
condition is signalled, the handler logs its message, and then THROWs or
INVOKE-RESTARTs from some safe place. If some UNWIND-PROTECT form in
the way finds itself in a bad state and signals a condition, well, that
gets handled too. Nothing is lost; everything is good.
-- [mdw]