|
ProposalForErrors
Distinguish expected errors from exceptions.
Proposal IntroductionChecked exceptions cause a lot of problems, and don't buy much, so Noop only has unchecked exceptions. Coders often use exceptions for expected error states. An example: // in Java
FileOutputStream out;
try {
out = new FileOutputStream(new File("/tmp/foo"));
} catch (FileNotFoundException e) {
// handle it
}
out.readline();Using exceptions to check for "expected" or "likely" error conditions is usually not an appropriate use of exceptions. Creating the exception requires capturing a stack trace, and the try-catch block interrupts the control flow and creates the ugly scope, in order to provide a reference to the exception. In this example, it requires declaration of the variable out in a null state so it must be mutable. This Java code could check for the file existence before trying to read from the file. File file = new File("/tmp/foo");
if (file.exists()) {
FileOutputStream out = new FileOutputStream(file);
out.readline();
} else {
// handle it
}But, this requires the File API to do some work twice. Some API's are especially bad for this sort of work, like having to run two database calls instead of one just to support this conditional. This proposal allows a way to hand an error back from a single API call in a more declarative way than with an unchecked exception, without the scoping issues and stack trace creation in an exception. DetailsIn the spirit of C's error out variables, some methods may declare that they return an optional error in addition to the returned value. This allows the caller to handle the error in a normal control flow. Here is an example with another common case, parsing a number from a String. A failure to parse is an expected error. class Int() {
Int, Error parse(String s) {...}
}
Int a, Error e = new Int().parse("13a");
if (e.isError()) {
// handle error
} else {
// a must be a good int
}We can return some sentinel value for a in this case, like zero. This has some advantages if the caller doesn't care to check this for an error, ie. there is some other validation of the value. The caller should be able to ignore the error, also. Int a, _ = new Int().parse("100");
|
I think with this proposal you are going back to the terrible API of C standard libraries, even though you may not want it, others will do it for you!
What you describe in your Java example IS an exception and should be handled as such, because you should not call new FileOutputStream?() before checking the File exists.
The only problem with the Java API in this case is that IOException is checked so you HAVE to deal with it even though you don't want to (because you consider you dealt with it when first checking that the file existed)!
Yeah I'm not really fond of this one even though I wrote it up. It bothers me that there's no real distinction between Errors and Exceptions.
I think we agree that exceptions will be unchecked, so that may encourage people to do the right thing and check that the file exists first.
Maybe we leave it up to the libraries to have exception or error "based" methods.. For example,
Int a = new Int().parse("a"); // Throws an exception Int a = new Int().parseOr("a", 0); // a => 0Or we have a way to catch and return a value in case of an exception at runtime, though feels bad.
Int a = new Int().parse("a") or 0; // Or even Int a = a or 0;(like ||= in Ruby).
What about the case when the file doesn't exist at access time? After all, checking for file.exists() does not guarantee that it will exist, or be accessible when you ask the file-system for the handle. So maybe it's an "exceptional" case after all?
I think the main annoyance is the chunky boiler-plate that comes with try/catch/finally blocks, and it's worth looking at alternative ways of handling exceptional cases. I like the elegant propositions by gabrielh, where you specify the default values for when an exception has happened. Why not try something similar for file API?
FileInputStream in = new FileInputStream("/maybeExisting.file", OnFileNotFound.EmptyDataInputStream); // No language intervention, just library support.The library could provide a set of possible resolutions for the anticipated exceptional cases, and clean way of handling the rest of unhandled cases.
FileOutputStream out = new FileOutputStream("/maybeExisting.file", OnFileNotFound.MakeDirsAndCreateFile, // Directory did not exist? try making it ... new FileNotFoundLastResortHandling()); // When everything fails call this // Some modularity potential ... // FileNotFoundLastResortHandling.java class FileNotFoundLastResortHandling implements ExceptionHandler{ @Override public void handle(FileNotFoundException e) { //log it ... //rethrow throw new FatalRuntimeException(e); } }Anyway, having a checked FileNotFoundException in my opinion, is a good way of making sure the developer does the right thing and handles the case. I doubt unchecked exception will encourage anyone to check for file existence, probably right the opposite.
The finally{ close(); } idiom could be cleaned-up with a "with" statement such as the one proposed in Python 2.5(The with statement). I think there is a similar effort in JDK7.
What if we ONLY had unchecked exceptions.
A consequence would be that in coding methodA() (which calls methodB(), which calls methodC(), and so on, to methodQ() which throws a specific exception), how would I know in methodA() the types of exceptions that I would be expecting to be thrown?
It seems to me that a method signature needs to be able to specify the types of exceptions that are expected to be thrown, even as only pure documentation. You can think of it as part of the contract that is being entered into between the caller and called software.
Could we consider exceptions to be enforceable documentation which we can optionally choose to catch?
You could have bearable checked exception if you used "abstract functions" (maybe not the best name).
Like a class which can be defined as abstract (allowed to be defined, but not used unless completed lated), an abstract function can be defined, but not yet used as is. She may only be called in the scope of another abstract function or a scope catching all checked exceptions the function may throw (either itself of because of other abstract function within its code).
Abstract property may be either explicitly written or infered (you union the exception-set of all function called in each try/catch scopes, remove the caught types, and union again till reaching the whole function scope, if non-empty the function is abstract and the computed set is its exception-set).
"Checked exceptions cause a lot of problems, and don't buy much, so Noop only has unchecked exceptions. " that will be a terrible error! the essence of a checked exception is that the caller does not have means to "know" about the conditions that will provoke an error. If he must know the state of a precondition then this is a runtimeException that the code will throw! once you know this don't tell me checked exception are a problem!!! now you may add features about preconditions/contracts ... but throwing out checked exception will be a very grave design error
I've been reading a lot about checked vs. unchecked exceptions lately. This isn't my field, but I'd just like to offer some questions/comments.
Quick question. With the above proposals (original and gabrielh), how would unhandled runtime exceptions propogate? Since they aren't explictly thrown, they're just set as scope variables. Some people argue for specific exception types. But in my experience, most calling code trys to discern 1 thing. Is it a problem I can recover from or not? To try and clarify a little:
So in my line of thinking, checked exceptions force a caller to consider whether it can handle any problems up front. You're more likely to include exception handling code because it's part of fulling the method signature. Only we've seen in practice that this is not the case. In many cases, the try/catch structure encourages programmers to treat all exceptions as "unfixable". Basically instead of handling the exception, most try/catches do one of the following.
So if you buy all of this (and I'm not sure i do, this is just a line of thought) it looks like checked exceptions aren't that helpful. But having a mechanism for explicitly specifying the exceptions that are possible is very helpful.
Marco.rogers has given me some food for thought. Most 'catch' blocks in Java are not terribly interesting. Typically, there is only a small range of things that you could gainfully do. And we all know that junior developers can do it very poorly, often writing empty 'catch' blocks, where thrown exceptions are just caught silently.
Now, to specify the contract that a method implements, we need to say something about 1) the method name, 2) the method parameters, 3) the method result(s) and 4) the types of exceptions that are expected to be thrown. If we were to make a complete implementation, we might also specify pre-conditions and post-conditions of the method itself.
So the method signature can say something important about the exceptions that are expected to be thrown. So, perhaps we should consider reworking the 'try/catch' block to make life simpler. Perhaps it is not checked/unchecked exceptions that is the language problem but the way our 'try/catch' blocks work.
What about something like:
try { .. some work in here ... } ignore (FileNotFoundException ex) rethrow (IOException ex) { if (printStream != null) { printStream.close(); } } wrap (Exception ex as new ApplicationException(ex.getMessage(), ex));Perhaps if we specialized the types of 'catch' blocks we permit, our junior developers wouldn't be able to write dubious exception handling code in the first place.
What do you think?
It seems to me that the argument over checked/unchecked exceptions goes something like this:
For use of checked exceptions:
For the use of ONLY unchecked exceptions:
It seems the argument comes down to one of 'correctness' versus 'convenience'.
(Guess that implementing the system so that it will be performance friendly while allowing to add metadata about the exception in a genenal is not easy)