I don't know whether or not there is a theory, but there may be an emerging pragmatic experimental science.
The best source I can think of is Bjarne Stroustrup, The Design and Evolution of C++, Addison-Wesley, 1994. If I remember correctly (it's a very good book and people keep borrowing it from me and not returning it, so I don't have a copy at the moment) there's a chapter about exceptions. The C++ committee under Stroustrup required a lot of empirical evidence that a proposed feature was necessary before they were willing to add it to the language definition. The Wikipedia page about exceptions has the following quote from that book:
At the Palo Alto [C++ standardization] meeting in November 1991, we heard a brilliant summary of the arguments for termination semantics backed with both personal experience and data from Jim Mitchell (from Sun, formerly from Xerox PARC). Jim had used exception handling in half a dozen languages over a period of 20 years and was an early proponent of resumption semantics as one of the main designers and implementers of Xerox's Cedar/Mesa system. His message was termination is preferred over resumption; this is not a matter of opinion but a matter of years of experience. Resumption is seductive, but not valid. He backed this statement with experience from several operating systems. The key example was Cedar/Mesa: It was written by people who liked and used resumption, but after ten years of use, there was only one use of resumption left in the half million line system -- and that was a context inquiry. Because resumption wasn't actually necessary for such a context inquiry, they removed it and found a significant speed increase in that part of the system. In each and every case where resumption had been used it had -- over the ten years -- become a problem and a more appropriate design had replaced it. Basically, every use of resumption had represented a failure to keep separate levels of abstraction disjoint.
In C++ the real win is RAII, which makes it much easier to handle resource deallocation during errors. (It doesn't do away with the need for throw
and try
-catch
, but it means you don't need finally
.)
I think the thing that convinced them they needed exceptions is generic containers: the container writer knows nothing about the kinds of errors that the contained objects might need to return (much less how to handle them), but the code that inserted those objects into the container must know something about what those objects' interface is. But since we know nothing about what kinds of errors the contained objects can throw, we can't standardize on exception types. (Contrapositively: if we could standardize exception types then we wouldn't need exceptions.)
The other thing that people seem to have learned over the years is that exception specifications are hard to put into a language correctly. See for example this: http://www.gotw.ca/publications/mill22.htm, or this: http://www.gotw.ca/gotw/082.htm. (And it's not just C++, Java programmers also have lengthy arguments about their experiences with checked versus unchecked exceptions.)
A little on the history of exceptions. The classic paper is: John B. Goodenough:"Exception handling: issues and a proposed notation," Commun. ACM 18(12):683-696, 1975. But exceptions were known before that. Mesa had them in about 1974, and PL/I may have had them too. Ada had an exception mechanism before 1980. I believe that C++'s exceptions were most influenced by the experience with Barbara Liskov's CLU programming language from about 1976. Barbara Liskov: "A history of CLU," in History of programming languages---II, Thomas J. Bergin, Jr. and Richard G. Gibson, Jr. (Eds.). pp. 471-510, ACM, 1996.
LookupError
would perfectly fine for every custom container, but I lot of people do not even know it exists. $\endgroup$