Re: The origins of CL conditions system
- From: Kent M Pitman <pitman@xxxxxxxxxxx>
- Date: 30 Nov 2007 02:48:56 -0500
Damien Kick <dkixk@xxxxxxxxxxxxx> writes:
bob_bane wrote:
On Nov 27, 9:06 am, Rainer Joswig <jos...@xxxxxxx> wrote:
One of the best examples was that the feature that you canI refer to that book as Stroustrup's "Design Rationalization and
continue from an error (and the handler is called
without unwinding the stack) didn't make it into the C++ standard.
Though Stroustrup (Design and Evolution of C++) reports
that they had people from Texas Instruments with Lisp
Machine experience explaining the Lisp Machine error handling.
According to the TI guys (can't remember the exact wording, don't have
the book handy), this feature was not used much. So it did
not make it into C++.
Mutation of C++". That's section 16.6, "Resumption vs. Termination".
At a meeting in 1991, several people who had had long experience with
languages with continuable exceptions (including Mary Fontana from TI
and Jim Mitchell from Sun/PARC) basically said "continuable exceptions
are not particularly useful except for debugging, and they allowed us
to write bad code."
Here is a bit more complete a quotation:
<blockquote cite="http://www.research.att.com/~bs/bs_faq2.html#resume">
Why can't I resume after catching an exception?
In other words, why doesn't C++ provide a primitive for returning to
the point from which an exception was thrown and continuing execution
from there?
Basically, someone resuming from an exception handler can never be
sure that the code after the point of throw was written to deal with
the excecution just continuing as if nothing had happened. An
exception handler cannot know how much context to "get right" before
resuming. To get such code right, the writer of the throw and the
writer of the catch need intimate knowledge of each others code and
context. This creates a complicated mutual dependency that wherever it
has been allowed has led to serious maintenance problems.
I seriously considered the possibility of allowing resumption when I
designed the C++ exception handling mechanism and this issue was
discussed in quite some detail during standardization. See the
exception handling chapter of The Design and Evolution of C++.
If you want to check to see if you can fix a problem before throwing
an exception, call a function that checks and then throws only if the
problem cannot be dealt with locally. A new_handler is an example of
this.
</blockquote>
Agree with this point of view or not, I do respect the approach of
having actually asked people with experience using languages which
supported "resumption" to try and help inform the decision. Kent, if
you have the time and/or energy, I would be interested in reading a
response to some of the views expressed by Stroustrup in this quote.
Of course, I should probably go back and read your paper(s) on the
subject, at which I had only glanced in the past, before asking you to
possibly repeat yourself. I have Peter Seibel to thank for
introducing them to me in _Practical Common Lisp_ in such a way that I
started to understand how they are different from C++/Java style
exceptions. I do not really have any practical experience with Lisp
conditions.
Well, I think he got bad advice or made some bad decisions, but anyway,
for whatever reason, I just disagree with the choice he made. (Though I
applaud his willingness to document his reasons so we can all discuss
them. One reason I have a lot of thoughts on things is the number of
decisions of my own and others that I've seen gone awry, so don't take
anything I say to be a criticism of him. I just don't like the choice in
this case.) But here are some somewhat hastily tapped out remarks, probably
not as well thought through as what he wrote even. As usual, sorry for
typos, etc. Ask me if I left something unclear.
If I write
{ f(x); g(x); }
in a traditional language, you could get all alarmist and insist that
f must never return because there's no guarantee that g will do the
right thing .. perhaps it doesn't expect f to return.
I think the great insight of the New Error System (on the Lisp Machine)
was that there was nothing special about returning from an error that
distinguishes it from any other kind of return.
Part of the documentation of a function is that it either is or is not
expected to return. So error does not return and signal does, and people
choose which they want to call based on their willingness to fall through.
But certainly the creation of a restart point is a proof that there's
a willingness to return, so I just don't see why that's an issue at all.
Another possible way of restating what Stroustrup is saying, and I don't
mean to put words in his mouth--I'm just speculating trying to figure
out what he meant--is that "people shouldn't program dynamically twisty
combinations of function calls and closures". That would again be
ridiculous, so I hope he's not saying that. If you look at the sample
code for the condition system you'll be struck by how it's nothing more
than a bunch of very straightforward function calls and macros. So the
question is how that could be dangerous without, basically, all function
calls and/or macros being dangerous. I just don't get it.
I have for a long time described restarts as being just
continuations+reflection. They happen at a point in a program when
the choice point is found but you don't know which continuation to
invoke. But surely there's nothing wrong with invoking a continuation
any more than returning or any more than invoking a delegate.
And it would work fine with strong typing, too, since under each
protocol there is a specific set of arguments that things take.
What he may be referring to is that C++ made a mess of the unwind-protect
operation, which you have to kludge up (as boost does) through destructors
on stack-allocated objects. That may mean implicitly that people who were
not maintaining decent stack discipline were rudely exposed by injecting
restarts too easily into code that was not prepared for that. But I don't
think it's fair to lay the blame on restarts for that, since the real blame
if that's the case goes to the language for having created a non-modular
case.
I'd liken it to the practice I've seen at some point in the past of
vendors saying "we poll for interrupts, and only in certain specific
and predictable places, so it's ok to not write without-interrupts
if you're in what wants to be a critical section because you're implicitly
in one just because the implementation promises". That's just broken,
since the implementation should make you write the without-interrupts
(or without-preemption or whatever) and then it should optimize cases
where it knows that the rule allows it to be removed. Doing anything else
means that if someone goes to change the compiler, all code will break.
And yet, it's not fair to say "that means it's dangerous to poll in other
places than were originally thought of". What's dangerous is, instead,
to tell people they can rely on contingent truths [as per Kripke].
It is completely ridiculous to claim that any materially useful
protocol can be built on his workaround suggestion:
| If you want to check to see if you can fix a problem before
| throwing an exception, call a function that checks and then
| throws only if the problem cannot be dealt with locally.
since the WHOLE POINT of the error system is not to add functionality
but to add protocol. And protocol is not about calling your own functions,
it's about calling those supplied by someone else, someone who didn't
conspire with you in the design, but rather plugged into a framework
on the promise there would be someone at the "other end". By not providing
a way to register something that could do this check, and by not providing
a way to search the registered checks, he has forced each user into doing
precisely what each user is not empowered to do: to write their own
condition system.
When I wrote the sample implementation [1] of Revision 18 of the CL
condition system ages ago there was no part I couldn't write in terms
of the actions I wanted to happen.. the only part I couldn't write was
"making everyone call my functions". That part has to be given by the
system. And if it is not, that's the end of the discussion.
There may also have been an issue for C++ that passing arguments over the
control return was hard. That was before delegates, and it is such a
pain in the neck to use parameterized functions that I can easily imagine
someone not wanting to bother (though I assume Stroustrup could have
managed it). And macro abstraction in C++ is a pain, which could have
contributed, too. So maybe again he was really complaining about how
darned cumbersome and inflexible C++ is syntactically. I'd agree with
that if he wanted to say that.
C# could have, and should have, done better. (I'm not a big fan of
static languages, but I do use them, and among that space of
languages, I really like the design of C# as a local optimum in a
design space I wish I didn't have to spend much time in). It's a bit
disappointing that C# didn't get this right because it did make some
good decisions and really thoughtful decisions on a number of other
things. Maybe it's because the CLR has no support for it. Ditto with
the JVM and Java. Alas.
But nothing that any of these languages couldn't just suddenly fix if
they get a mind to.
Meanwhile it underscores my point about why I stubbornly stick to
Lisp. It's not that I wouldn't use something that came along if it
was just as good. It's that people seem so far intent on not making
something that's just as good. I still find many useful things in
Lisp, and particularly Common Lisp, that are not picked up elsewhere.
When there's a legitimately better Lisp dialect, maybe I'll use that.
When there's a better language, why wouldn't I use that? One reason I
wrote the 2001 condition paper [2] was that it was for a forum that
would not just be about Lisp, and I thought maybe it would help some
of the ideas get out. And maybe ultimately they will. It sometimes
takes time. Meanwhile, I continue to use and like CL.
[1] http://www.nhplace.com/kent/CL/Revision-18.lisp.txt
[2] http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html
.
- Follow-Ups:
- Re: The origins of CL conditions system
- From: Maciej Katafiasz
- Re: The origins of CL conditions system
- References:
- The origins of CL conditions system
- From: Maciej Katafiasz
- Re: The origins of CL conditions system
- From: Kent M Pitman
- Re: The origins of CL conditions system
- From: Rainer Joswig
- Re: The origins of CL conditions system
- From: bob_bane
- Re: The origins of CL conditions system
- From: Damien Kick
- The origins of CL conditions system
- Prev by Date: Re: Reasons to choose CLISP over other free implementations
- Next by Date: Re: The origins of CL conditions system
- Previous by thread: Re: The origins of CL conditions system
- Next by thread: Re: The origins of CL conditions system
- Index(es):
Relevant Pages
|