The Scourge of Error Handling
CowboyRobot writes "Dr. Dobb's has an editorial on the problem of using return values and exceptions to handle errors. Quoting: 'But return values, even in the refined form found in Go, have a drawback that we've become so used to we tend to see past it: Code is cluttered with error-checking routines. Exceptions here provide greater readability: Within a single try block, I can see the various steps clearly, and skip over the various exception remedies in the catch statements. The error-handling clutter is in part moved to the end of the code thread. But even in exception-based languages there is still a lot of code that tests returned values to determine whether to carry on or go down some error-handling path. In this regard, I have long felt that language designers have been remarkably unimaginative. How can it be that after 60+ years of language development, errors are handled by only two comparatively verbose and crude options, return values or exceptions? I've long felt we needed a third option.'"
Ignoring the error completely, data integrity or planned functioning be damned.
Monadian beef bbq.
I think MS already tried the blue screen.
BASIC has had it all along!
What a fool believes, he sees, no wise man has the power to reason away.
if (errno) goto error_handler;
error_handler:
if (errno == ENOMEM) enomem_handler();
void (enomem_handler) (void) {
char *error_msg = "No more memory, aborting.\n";
write(STDERR_FILENO, error_msg, strlen(error_msg));
_exit(1);
}:
Exceptions should NOT be used for 'normal' errors. They should be used for events that are, well, exceptional. A healthy program should NEVER raise an exception, but may deal with a lot of error conditions.
While I have seen good error handing schemes in many languages, so far, I haven't seen anything as good as C++ exceptions combined with RAII. Exceptions alone aren't that great, but if you combine it with the way constructors / destructors work and compose in C++, it ends up working really well. A lot of languages with exceptions lack RAII. Java and C# have exceptions but don't have destructors (the language equivalent is much less useful than C++) much less ones that compose.
The only real problem is that lots of C++ code rely on return codes, no error handling at all, or poor use of exceptions and resource management. There are lots of C++ programmers who stumble on error handling code and haven't learned how to take advantage of the tools the language provides. Of course error handing logic can be quite hard, even if the language helps out a lot.
STM is also a great way of doing error handling. Transactions (like used in databases) make error conditions much easier. But they cannot be limited to databases; transactions in the file system (Microsoft has this with NTFS) and transactions in memory data structures (STM) are very valuable.
Visual Basic had:
On Error Resume Next
I last typed that when I was about 13...
The documentation shows a couple of valid uses for it.
That was the low-level C solution, which also doubled as a standard interview question throughout the '80s.
I don't know if Dennis R. was to blame or not.
Normally exceptions should be used in exceptional cases, not in normal control flow. Exceptions are usually quite expensive, especially in C++ compared to just returning an error code. Language APIs should be fast, but also convinient so they had to made a trade-off.
In Soviet Russia, third option errs on you!
It is not clutter. It is necessary. Trash cans in the home might be considered clutter too I suppose. Some people artfully conceal them within cabinets and such, but in whatever form, they are both necessary and either take up space or get in the way or both.
It is the reality we live in. If you want to code in a language that doesn't require error handling, you might look to one of those languages we use to teach 5 year olds how to program in.
Good code does everything needed to manage and filter input, process data accurately and deliver the output faithfully and ensuring that it was delivered well. All of this requires error checking along the way. If you leave it to the language or the OS to handle errors, your running code looks unprofessional and is likely to abort and close for unknown causes.
I think the short of this is that if anyone sees error checking as clutter or some sort of needless burden, they need to not code and to do something else... or just grow up.
How can it be that after 60+ years of language development, errors are handled by only two comparatively verbose and crude options, return values or exceptions?
Of course, it could be that this just means that your own language horizons are too narrow. Prolog and icon come to mind.
Lacking <sarcasm> tags,
"Never test for an error condition you don't know how to handle." -- Steinbach's Guideline for Systems Programmers.
Non-Linux Penguins ?
...like the one described here:
http://www.reddit.com/r/ProgrammingLanguages/comments/m0f1o/how_about_a_programming_language_in_which/
In this way of programming, errors are just another set of events.
Exceptions here provide greater readability
Nah, they don't.
How can it be that after 60+ years of language development, errors are handled by only two comparatively verbose and crude options, return values or exceptions? I've long felt we needed a third option.
Maybe - and admittedly this is just a guess from my fairly ignorant viewpoint - it's a very hard problem. How can it be that after 100+ years of industrial development, we're still heavily reliant on internal combustion engines to get us around? Why have we only got people as far as the moon in 60 years of space travel? Why, after x years, have we only achieved y?
Because that's the way it is. Is there some reason we should have the third option by now?
systemd is Roko's Basilisk.
The author commends the use of multiple return values and a side-band error value that must be checked? Gee, multiple return values have been in Lisp forever, and maybe he's not aware of this little thing called "errno"?
Error handling is very, very tedious by nature. There are bajillions of ways that a system can go screwy, and many of these have individualized responses that we want distinguished for it to behave intelligently in response. We expect computers to become "smarter", and that means reacting intelligently to these problematic/unexpected situations. That is a lot of behavioral information to imbue into the system, all hooked into precise locations or ranges for which that response is applicable. That information is hard to compress.
The key to taming exceptions is to use them differently. Any exception that escapes a method means that the method has failed to meet its specification, and therefore you will need to clean up and abort at some level in the call chain. But you don't need to catch at every level (unless your language forces you to), nor should you need to do anything that relies on the "meaning" of the exception. Instead, you take a local action: close a file, roll back the database, prompt the user to save or abandon, etc, and either re-throw or not according to whether you have restored normality. There will only be a few places in your app where this type of cleanup is needed.
If you're not doing it this way, you're using exceptions as a control structure, and that's never going to be clean.
Paid Q&A/Research
We should also add to this discussion mention of Ken Pitman's and others Condition System from Lisp languages. It can be read about here:
http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html
and
http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html
It's easy to say something is crude, inefficient etc. How about proposing a solution ? I find exceptions quite good. The only problem I have with exceptions is to decide on what level to put try block and where to handle the exceptions. For a single program this might not be a big deal but with code reuse it becomes a problem to memorize what scheme I have chosen months ago for particular piece of code and particular exception. For example opening a file can be handled internally by reusable piece of code that might display message box and loop back to opening file until user enters the right file. But one can chose to pass the exception to the calling program instead. If it would be just this one that would be no issue to describe it in the comments but there are many types of exceptions and some can be handled locally or not. With multiple exceptions some created by the author, others generated by the framework building a reasonable reusable scheme is a problem by itself.
JAM
Tag it, bag it, log it & blog it.
Why should Anti-virus companies have all the fun?
Condition handling and Conditions. Old school, but does the job without too much clutter!
Things either work, or they do not. The function either completes, or it throws an exception. You can catch this, or you can choose not to.
I want to delete my account but Slashdot doesn't allow it.
Even if there was a shortcut for safely ignoring return values, I would (the company I work for would) still need to check and catch every return. Why? We have to log them all.
If you don't want to deal with failed returns, I find that a scripting language is the best way to go. I write my glue functions to handle nulls gracefully and I am done.
This is the real third option.
The so-called "error returns" from things like file opening are telling the program something very important about what's going on. The program's flow must be designed from the beginning to interpret and handle errors. This is in fact be much of what a good program does.
It doesn't matter whether we use exceptions or error codes to signal the errors as long as the program is designed to accurately interpret the errors that do occur. In some sense, exceptions may be easier to implement in today's event-driven interactive interfaces. Regardless, though, the design must not allow errors to be lost.
Was it Cooper in "About Face" who said that an error alert pop-up was essentially an admission of failure on the part of the programmer?
Rick.
The Try/Catch block is an error handling paradigm that should never have existed. It creates nothing but programmers who have absolutely no idea how to code. A lot of coders just cover entire routines of code in t/c blocks because they don't really want to handle errors at all. T/C blocks also prevent you from seeing real problems in your code. A good coder knows to pre-prepare for errors and handle errors where they happen, and nowhere else. The T/C blocks also create needless code block indentation and much slower inefficient code. I have nothing but disdain for any code that crosses my desk that contains T/C blocks. It tells me that you just code recreation-ally and never intend to be a true programmer.
The fact of the matter is that errors happen, and if you have a program that needs to continue functioning in face of them (until things REALLY go sideways), then handling errors will be 50-80% of your code. I have been developing HA (high-availability) large scale distributed systems for 30 years, and I have not yet figured out a way to reduce that "cost". If your software can afford to stop when some unhandled exception occurs, then don't bother handling it. If your software has to run 365x24 or people will die or factories will stop grinding out "stuff", then you have little recourse other than to anticipate all possible conditions that will affect the function of the program, including restarting it if necessary.
Also: try { throw ImportantException(); } finally { throw UnimportantException(); }; The unimportant exception wins again.
try { throw HolyFuckTheWorldsComingToAnEnd(); } finally { throw PayMeMegaBucksToFix(); };
But even in exception-based languages there is still a lot of code that tests returned values to determine whether to carry on or go down some error-handling path.
The whole idea of exceptions is that you don't need to worry about checking return codes. If you're putting a lot of work into checking return codes for error conditions, then you're working with some ugly code that probably needs refactoring. One place I see this a lot is in Java wrappers around libraries ported from C (or used directly with JNI). Often, to make the documentation and example code line up perfectly, the wrapper returns invalid values for exceptional circumstances rather than just throwing exceptions. This is wrong.
The only code that should be run routinely for error-checking is to see if an exception should be raised. That's fast, only needed once per major function (so you're not re-checking at every minor transformation), and doesn't incur the cost of exceptions unless they're actually raised (which should be fairly rare, as they're exceptional).
You do not have a moral or legal right to do absolutely anything you want.
Monads are fun for error handling. :)
I donno if they present exactly what the author might consider a third option though, well certainly they can present other options, like with the Either monad, but that's no simpler really.
The Christian religion has been and still is the principal enemy of moral progress in the world. -- Bertrand Russell
Just don't bother checking for errors.
1984 was not supposed to be an instruction manual.
Programmers like to be able to see the logical flow of the program, and error handling messes that up. The problem is that error handling is half of the logic of a program. Hiding error handling logic in exceptions frequently mainly seems to be way to let poor programmers think even less about the unusual cases.
Anybody who wants to build truly error-tolerant systems on the java virtual machine should take a look at AKKA: www.akka.io.
They have a very refined approach for error handling that does not require you to clutter your code with try { } catch { case x, case y, ... }.
The biggest problem with exceptions is that they get thrown too far, changing them into comefroms (the opposite of a goto). And like gotos, they encourage spaghetti code. The best way to deal with them is to limit them to thrown exceptions only to their callers. That way, all exceptions become part of the subroutine's interface. Remember, for a programmer, out of sight is out of mind. If it's not part of the interface, it will be forgotten. For those who are interested, you can read my blog for details and an example.
Don't stop where the ink does.
If possible, design a component such that no errors will ever occur (except for hardware failure). An entire program can't be designed that way, obviously, but individual components can be. Collecting all resource allocation into a small, well defined set of locations will relieve the majority of other code from the need to handle errors. In such a design, the majority of functions will return "void".
Normally exceptions should be used in exceptional cases, not in normal control flow.
People keep saying that, but I've yet to find someone who can defend the position with a logical argument.
Fundamentally, you run some code to do a job. There are two ways it can finish early: either it succeeded, and we did all the work/figured out whatever information we were asked for, or it failed, and maybe we want to report this along with some related information. Either way, there is nothing useful left to do except hand control back to the higher level code that asked for the work to be done, along with the outcome of that work, as efficiently as possible without leaving anything in a mess as a side effect.
Exceptions, as provided in many mainstream programming languages today, could serve either purpose just fine. The semantics work the same way in each case. The performance implications are the same in each case. Aside from the unfortunate name "exception", which we could replace with something like "outcome" or "result" just as easily, and the commentary of certain commentators, whose arguments are rarely more than an appeal to their own authority, there is no difference between the two cases semantically or in terms of the code I want my computer to run.
So, why should exceptions be used only in exceptional cases, apart from dogma or convention? They're just a tool, like variables or functions.
Exceptions are usually quite expensive, especially in C++ compared to just returning an error code.
I'd like to see your profiling results to back up that claim. I've got a few years of working on high performance code that suggests most compilers from the past decade or more use some variant of table-based dispatch to handle exceptions. That means they will not need to manually unwind the stack step-by-step in the case where the exception is thrown/raised; they can just run any necessary clean-up handlers and otherwise skip over everything between throwing the exception and catching it. It also means there will be less error checking code required all the way up the call stack in every other case. In other words, this model runs faster and it does does so whether or not an exception is thrown. The overhead is in the space for storing the jump tables and the compiler's effort to generate them (both of which can be unpleasantly large) but not in the run-time speed.
If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
The nice thing about functions (rather than just simple subroutines) is that you can chain them in a single line. E.g.
a = geommean(factorial(b), zeta(c))
The single return value mechanic really makes it easy to use for math-style expressions. But the single return value mechanic isn't adequate when the function is allowed to have errors. In a language like c, one might do something like
f = factorial(b, &error);
z = zeta(c, &error2);
if (error==0 and error2==0) { a = geommean(f, z, &error3); } else {error3 = 1;}
Unfortunately, there's no longer any simple way to chain the functions, which kinda defeats the point of functions. So exceptions were invented.
Perhaps a better language would have some better handling of multiple-output functions. Maybe some way of chaining functions such that the error behavior can be defined. I'm not sure exactly how to do that.
There are two ways to do error-handling: try{}catch{}, or if{}else{}. That's "using exceptions" and "using return values", under Dobb's naming.
The difference in usage is simple: one handles errors immediately, thus cluttering the code with all the things that could go wrong, while the other separates error-handling out, pushing it to the end of a block (and away from the code that actually generates the error, which can complicate debugging).
I can really think of no other way to do it. You can handle the error where it happens, or handle the error at the end. I tend to look on anyone whining about how hard error-handling is with suspicion - their suggestions (if they even have any) are almost always "the language/compiler/interpreter/processor/operating system should handle errors for me", and there are enough obvious flaws in that logic that I need not point them out.
..you are an idiot. Proper software diligently checks the error code of every method called. Real software is in the size of 50k to 50M lines of code and even star programmers sometimes make a mistake which will eventually be signalled by some error condition, probably a method/function which trips over some bad state. That error must be detected and reported.
Check out Haskell's Error Monad transformer. Used properly it is a beautiful thing.
The failure of an attempted operation may or may not be an error. If I want to allow the user to override the default value by means of an environment variable, I call getenv(3c). If the call fails I use the default. If it succeeds I use the value that is returned. On the other hand, I might want in some instances to require that an environment variable be set in which case failure *is* an error.
Spreading the logic out over several files doesn't help anyone other than people who shouldn't be programming in the first place.
Having worked w/ a large package that "helpfully" implemented automatic "error" checking I despise it. The result is bad error handling.
Programming is all about handling edge cases correctly. It takes careful thought and lots of work.
...the problem is any one exception is a nice, elegant way to handle an exceptional condition, but non-trivial code has page after page after page after page of exceptions, usually for trivial things like converting a string to an integer. In practice, exceptions clog up code and make it difficult to follow. Also, how do you recover from an exception? If you try ... catch, then you recover and fix the problem, you have to somehow goto the start of the try again. Most languages with exceptions don't have any clean way to say "I fixed it, let's try it again".
Basically, it's easy to just punt and let Java (for example) dump page after page after page of stack trace to the console.
I'll take a return code any day over an exception mess.
I am a fan of return values and proper testing of these. But your statements about destructors are 100% correct. Java and C# have nothing equivalent and that is why they are a pain in the rectum.
People are building massive systems using plain C and these are highly reliable (see Linux, BSD kernels). Exceptions and STM seem to be for the quick-and-dirty-overpriced corporate hacks.
You don't write an article like this unless you're actually going to suggest a different solution in it. Otherwise it just comes off as whiny and inexperienced. "Oh, if only we could not do that thing that everyone must do if they want robust code!" Reminds me of beginner CS students who don't want to make an extra header file or prototype functions. We're not doing magic here, and no amount of wishing for magic will make it happen. Work with some magic module (ActiveRecord, maybe) for a while and you'll quickly learn to hate magic, anyway. Discipline is required to write code that will stand the test of time. If I were wishing for something, it'd be that more programmers had the discipline to write good code consistently.
I'm trying to teach myself to set people on fire with my mind... Is it hot in here?
And I must say that as the Editor in Chief he has a very simplistic view of the problem. If I understand, his view is that a global exception added at the compiler level would somehow solve all the problems. He gives the example of calling "open" without worrying about it failing. Of course he doesn't state how to handle the failure when it occurs. For example
open(file1); // ok // failure
open(file2);
What happens to file1 in this case? How is the code cleaned up? There may be a case where you don't want to just close all files in the functions, but just create file2 if the open failed. (for example).
His complaint is that there is too many options available for error handling, and that they lead to cluttered code. As far as I can see the alternative is not enough options available and code not always doing what you want, and having to fight the compiler in order to get what you want.
..for heroic rationality !
This is generally seen with asynchronous code, but it could apply anywhere.
Consider: (javascript) XmlHttpRequest has a readystatechange callback. Most javascript libs wrap it up so you pass in two callbacks, one for success and one for failure/error.
e.g.
jQuery.ajax(url, { ... }, ... }
success: function(data, textStatus, jqXHR){
error: function(jqXHR, textStatus, errorThrown)) {
);
No return, no exception, the programmer decides how to handle it.
Do you even lift?
These aren't the 'roids you're looking for.
We need the computer fairies to handle our errors, that way the beauty of our code will not be marred by mundane things like error checking.
Seriously, error checking is part of the process. It's not the fun part of the process but it's a necessary part. Return values and exceptions work just fine as long as you get off your high horse and realize that your code will not be hung in the Louvre. Working is more important than pretty.
Problems don't exist in reality, they exist in points of view.
Don't complain about syntax, grammar, or spelling. There is no.hell like input on android.
Programming languages should have the following built-in:
1. Logging (like Log4Net with choices of database, file logs, e-mail, Windows Error Log, etc.). All exceptions should be logged to the chosen log path automatically.
2. Web pages should go to an Error page, with logging and a link back to the home page. Also, they should record the yellow screen of death info like elmah automatically in the error logs, not show them to the user.
3. Popup dialogs on applications should be more user friendly, with the programmer info going into the logs. Microsoft should work on their error messages so that they make more sense to the user. "Directory not found" should instead say, "Application Name could not find the path c:\folder\subfolder. Please ensure that this folder exists or contact the software developer." Good default error messages would save millions of hours worldwide.
With these simple changes, the quality of software would improve substantially and the work of developers to catch errors would go down dramatically, if they were comfortable with the default handling.
Peter predicted that you would "deliberately forget" creation 2000 years ago...
That Mr Steinbach must be a fucking idiot. If I encounter an error I can't handle I have to log that and stop running. RIGHT NOW.
Quality code will always be "cluttered" with data validation code, result verification, and a host of other details.
The simple fact is that computers are stupid. They have to be explicitly told what to do in every conceivable situation the code could encounter at runtime, or else the code will crash and the user will complain about it being "unusable".
I notice that despite the article author's bitching about the situation, they had not one suggestion as to what to do instead. It's easy to bitch about life, but a lot harder to suck it up and deal.
If exception handling and return-value checking code are "too hard" for someone to understand, they need to get the hell out of the programming industry and leave it to professionals who actually find it fun and challenging to deal with all the details. Not everyone has the mindset of a true programmer.
I do not fail; I succeed at finding out what does not work.
I suggest the author has a talk with Alan C. Kay.
Once there was an research done in PARC about so-called "Aspect-Oriented Programming". The idea was to describe different aspects of the problem (in our case the business logic and the error handling) in different domain-specific languages, then use "compiler" to combine the two into actual executable code. As far as I know, the idea never got further beyond research stage. In any case, it's the only alternative to error codes and exceptions I've ever seen.
All coding should proceed as if every possible exceptional condition (device not ready, cache fail, controller failure, cat dials 911 on speakerphone) is the primary and intended purpose of the Project. Hash collisions not merely covered as a contingency but pursued with vigor in the main line to the Nth degree, where N indicates the infinitesimal possibility of multiple simultaneous hash collisions that would be the likely result of a vengeful god constructing the universe such as to produce a life of continuous and foul exceptions.
When gathered at the water cooler, coders would discuss triumphs in their particular areas of malfunction, and when they corroborate as a group it is to merge their respective threaded exceptions into a parallel paroxysm of failure, branching with virtual threads and physical coring such that the greatest possible number of malevolent conditions are met and coded for, simultaneously. Proceeding steadily towards the grail of the Grandest Failure.
The Grandest Failure being the stuff of mere legend, yet it is what drives us. It represents that supreme and sublime moment where everything that can go wrong has gone wrong and the very fundament reeks of wrongness.
Buffers are not starved as an exception, they are starved by design! Disk controllers are never ready. Communications packets never arrive in sequence, or so we assume because there are no markers to check, when they do arrive they are garbled beyond repair. Reconstruction occurs as a matter of course! Streams are unsynchronized by nature, incompatible by rote, unresolvable.
Off the corridor in a dusty hallway a small team of pariahs is assembled to perform the dirtiest and most detestable task of all: to handle the exceptions and branches thrown by the main line, conditional branches sketched out briefly (whose existence is known but not mentioned in polite conversation) are pursued in secret. This is necessary work but unrewarding as it leads away from the noble purpose of Grandest Failure, towards useful work. Such stuff as consolidation, transaction handling and data ordering, forgive me for uttering, Chaos be Praised!
For the goal is to produce a System that boldly and efficiently proceeds down the pathways of most numerous and most simultaneous failure, where the actual success of anything triggers the exceptions and is cast off to the side.
If robustness of design becomes human sentiment, it could be said that the System confidently strides forward boldly embracing every error condition and is shocked -- horrified -- every time something goes 'right'. As life's own experience is our guide, it is seldom disappointed.
The output of useful work in such a System the source of great embarrassment and discomfort, a necessary evil.
That is the principle behind the control systems of the Improbability Drive. It is the driving principle of the quantum flux, Brownian motion and wave/particle paradox.
All of this Order and Progress (blaspheme!) is but a side road off a side road ad infinitum. The main path leads to Chaos. Follow that path and revel in it. There is no honor in coding for success, any idiot could do that.
Down deep people know this is the Way. That is why when coders meet in dim conference rooms and the slideshow laptop suddenly projects a Blue Screen of Death for all to see, there is an eruption of thunderous applause, as if one had dropped a tray of food in a crowded cafeteria. Deep down we know failure is the noble path, and success the exception.
<blink>down the rabbit hole</blink>
These fields (web design and development, application and database programming, mobile work, and similar fields that involve complex programming / development strategies, etc.) have very little "oversight standardization" when it comes to quoting projects, resource management, assignment of workloads by supervisors, etc. and yet, the management bodies continue to expect spot-on approximations in regards to project durations. After 60+ years, this STILL causes problems with every one of us. And what eventually happens is that if the subordinate(s) can't meet deadlines, then the subordinate is canned and replaced whereby a mix and repeat cycle ensures... This kind of crap produces a horrible employer because no matter who your weak link might be, there's ALWAYS going to be a weak link out of any pack of developers due to the simple fact that no two developers have the same skill levels AND due to the fact that management keep pushing for smaller, faster, cheaper... It's a complete mess.
This, and the lack of job security due to no standardized approach for REAL invested employer continuity (i.e. - REAL training and not the bullshit you see during HR seminars or that crap we all occasionally go through with online courses that barely grazes the surface of topics we go experience in our jobs or stuff that deals with concepts that have no relationship with what you work on each day); this stuff takes a MUCH higher precedence concerning REFORM than some petty tangent someone came up with concerning exception handling in programming language...
That's the basic problem I see. For the example given of an open() call, what happens when the file doesn't exist? It could be an error, resulting in the program throwing up a message and aborting the operation. Or it could be a perfectly normal and expected occurrence, resulting in the program continuing as if it'd opened the file, immediately hit an EOF and closed the file. So with the proposed mechanism, every time I see an operation I need to jump to the bottom of the file and find it's error block to see if it's actually an error or not. That breaks a basic rule for coding: don't make the reader jump all over the place to see all of what's happening.
Worse, the same file-not-found condition could be an error at one spot in a routine and a normal result at another. That means you not only need the error-block construct, you need an additional construct at the point of call to associate different error blocks with the different open() calls. And you haven't eliminated the jumping-all-over-the-place problem.
Exactly. That was my first thought on reading this: "If there's a better way, show us. Come up with a solution. What's stopping you?"
In reality it's not that obvious, or someone would have thought of it already. I would look at engineering practices and see how they handle failure modes. Sometimes it is better to let the thing break as long as you design it to do the least amount of harm when it does.
It's possible to develop defect-free software as long as all factors are under your control. E.g. a program that runs on unreliable hardware can never be made reliable.
"Slow down, Cowboy! It has been 3 years, 7 months and 26 days since you last successfully posted a comment."
have a drawback that we've become so used to we tend to see past it: Code is cluttered with error-checking routines.
But that's precisely what functions are for: To encapsulate tedious clutter, and to give it an easily-understandable name that's simple to use at the next-higher level.
The lowest tier of my code is just a bunch of small wrapper functions -- each one does a simple job and also checks for errors. When it finds an error, it logs it (using another function at my lowest tier), and then recovers the best it can. (Most of my programs are services in embedded systems that MUST keep running at all costs. Even horrible situations like malloc()==NULL must be handled in a way that keeps the machine running the best it can under the circumstances.)
In general, I always try to segregate the complexity of error-checking to places where it's hidden from my eyeballs most of the time. The result isn't clutter at all. It's elegant, well-organized, and a pleasure to work with. Yes, it requires me to spend extra time writing that low-level tier of functions -- but there is no free lunch -- you gotta do that work somewhere, and burying all that complexity inside of easy-to-understand, reusable functions is a pretty nifty solution.
1. Smarter users.
2. Failing to get #1, go with (a) connect wireless-controlled shock collar to tender part of user's body, (b) toggle shock collar if any error is encountered.
If you don't start with #1, you'll either get there eventually via a Pavlovian response to #2 or the user will stop trying. A win-win situation.
Well in Java, there is RuntimeExceptions (meant to propagate up to the user, who can give it to support) and Exceptions.
You can handle Exceptions you can't handle by rethrowing them as a RuntimeException. RuntimeExceptions also do not need to be declared in the function signature.
Not that I do that often, I'm busy with PHP, where the exception handling is usually done like "Hey, you broke it!".
Hey don't blame me, IANAB
I mean given how often I see code from developers not use any of this anyway ("sarcasm on" I don't have time, I just have to write perfect code "sarcasm off") I wouldn't be too encouraged to implement something new an innovative. (Since even if it was awesome they'd probably not use it.) Gee can you tell I'm a developer that too familiar with people cutting every corner they can, even if it really doesn't save them any time anyway?
Did you know 80 to 90% of the moderators on slashdot wouldn't recognize a troll even if one dragged them under a bridge.
Copy-paste programming is always a definite sign of An Amateur At Work.
It doesn't matter whether we have one, two, or ten options: very few developers are conscientious enough to give a shit. Whether they're there just for a temporary job and don't care about quality, or they're trying to get back at a prick of a boss, or they just don't know or realize that they should handle unexpected results (aka. noobs!), until the language or CPU checks for us, our software is likely fucked.
I'm still haunted by the memory of too much medical software written by too many people where they check maybe one condition and continue on - it's all good. In one case, I noticed that a function could return a failure, and we never checked for it. I added a check for it and was told to take it out because we didn't know how such a check would propagate through the decision chain and affect system operation. God help you, Siemens $productName blood analyzer users!
Every time I write a new function, I think in terms of paranoia: "How can this fuck up, and what values should it return?" My code often focuses extensively on error conditions. I justify it this way: when you get a very specific error, you can bet it came from one of few places so it'll be easy to find and trace back in the code. If you get a generic error, good luck figuring that one out without lots of run data.
Yes, it's sometimes cumbersome, and it adds a few extra lines to the file. So what? Use your editor to hide the error-handling block, and if you're concerned about file sizes, buy a bigger disc. Software engineering requires that you, you know, engineer the software for robustness. It should be freakin' bomb-proof . Take a little pride in your work. If you find that unappealing, please, get out of the business.
When Windows does a bluescreen, this happens because the kernel is fucked up. It is better to crash it right now instead of corrupting even more files on your harddisk, disable vital security etc.
A major cause of BSODs are faulty drivers. Remember which piece of crapola you added to your system in the last few days and remove that. Then the BSOD will most likely go away, too.
Conceptually speaking, Failure Is A Necessity to achieve certain security properties. If you detect a dangerous situation the best thing is to shut down and wait for an expert to analyze the coredump/remains (whatever you call that in your system). Then the root cause can be fixed and the security guarantees can be maintained. You better want to shut your bank instead of continuing to run, despite of a known bankrobbery plan, right ?
When Chuck Norris throws an exception, it is always fatal.
How is the Riemann zeta function like Trump rallies? Both have an endless number of trivial zeros.
you know, error handling is just a problem of expressing control flow. multiplexing the return with if/then/else at all the call sites
is just a clumsy way of expressing that.
in some circumstances it might be cleaner to pass a non-returning function handler either as an additional forward parameter,
or tucked away in some state.
that way you dont have fold all those execution paths together all the way up the return path.
there's one. i don't know why people don't like it
I'm all for another option if it results in less cryptic error messages thrown to the user level. I don't know how many times I've had to help someone and told them don't read that error literally. what it REALLY means is this....because a single function spit out an error and was just passed up the chain until the user sees it ....
How about a language that takes into account that errors are more common than successes and throws successes instead of errors?
AB HOC POSSUM VIDERE DOMUM TUUM
think outside of the box of java, throw an event, think asynchronous messages.
with and using are excellent error handling mechanisms that have no clutter and clearly define the protected code. Most exception handling is little more than logging what went wrong since many exceptions really don't have solutions that can be coded for. The main thing we are trying to do in these situations is write out some data so we can debug and clean up any messes (open connections for example) we have laying around so the things don't get worse.
The third option is to ignore it. The fourth option is to misunderstand it. And the fifth option is to try to guess the intend of the user and bypass the error.
So I'll assume that the author's thought's, or lack of thought as he has been thinking about this for quite some time yet can not offer a single insight as to why he hasn't thought of anything is that the last option is to find a new career.
Monads monads monads monads monads from Haskell.
Or "workflows" in F#. Related to, I think, "generators" in Scala?
Roughly (and I'm going to make up some C++/Java style syntax here), you write something like this:
workflow someExpressionMaybeAnObject
{
int x = someassignment;
some statement;
someotherstatement;
}
At the end of each line, you check the return value for errors, and use the handlers defined by the object up top, which could short-circuit the rest of evaluation.
These are actually a lot more general than error handling. For example, they generalize Python-style list comprehensions when used in a certain way.
In Haskell-land, there's a lot of interesting math about how they work, but you don't need it for error handling.
Moral: Learn a wider variety of languages!
It's essentially "let's push forward and hope nothing breaks, and when something does, let's have some automated excepton handler handle it as an afterthought".
Just like VB6's ON ERROR GOTO stupidity.
It's your responsibility as a developer to properly test for error conditions and handle them in a thoughtful, logical way, instead of recklessly tearing through your code and having some trap you've set up say "oh dear, something seems to have gone wrong, let's just examine the mess we've made and try to piece together a fix, hmm?"
Testing for error conditions as you go also make it easier to handle and properly get around problems because you're taking care of them on the spot, rather than as an afterthought.
try/catch is an abomination that should be outlawed in programming.
Errors happen, and have to be handled. With code.
If you have an alternative method in mind, please share the secret.
Sheesh, evil *and* a jerk. -- Jade
I also see this more in Java due to the checked exceptions -- who hasn't seen something like // Make the compiler shut up
try {...} catch (Exception e) {}
So, you can write PHP in Java? Awesome!
W. RIchard Stevens had a pretty good approach to error-handling in "Advanced Programming in the UNIX Environment". In most cases, he wrapped system calls or library calls in wrappers that would test for errors and abort with an error message on failure. These wrappers meant that the main program could forget about error-checking.
In the relatively rare cases in which the main program felt it could recover from an error, it would call the system call directly and handle the error itself. This led to pretty clean but still very safe code.
Naturally, this approach is inappropriate for authors of library code, but it's great for the main body of an application.
Error handling is only needed if errors are possible. Make it impossible, using type checking and controlling the whole state space. Every function must always return _correct_ value for all parameter values. Reading files etc can never fail, but instead default to a file which is guaranteed to exists etc...
Just embrace the segfault.
For me it is hard imagine the use of exceptions...
I tend to plan out failure paths and non-resource consuming compensation paths ahead of time so when something does fail you are prepared for a meaningful response.
If you follow simple organization rules and route your shit to minimize heap allocation not only is your code more performant there are many less return codes to check, u get more options for taking action in a environment where everything except the northbridge could at some point be expected to be broke.
For example reporting out of memory is quite a useless gesture if it requires memory allocation to send that message or reporting I/O has stalled thru that same stalled I/O channel. With a bunch of the managed and exception only languages such planning is simply impossible.
Return codes are easy to systematically verify coverage checking with a few simple tools or even just eyeballs. With exceptions you have no idea what can throw what and very likely neither does the person who wrote it.... there is no concept of operations which inherently cannot fail unless the execution environment itself was compromised. In the land of exceptions anything could throw an exception at any time and you have no way of knowing jack diddly about these possibilities. Throw is simply a fancy alias for goto and it sucks just as hard. You are fucked if you just layed a lock, one of those land mines goes off and the higher layer decides to *handle* the exception... The necessary freqeuncy of try catch blocks to gain parity actually make return codes look appealing.
Really these exception schemes only work at all to any degree if the environment is managed/garbage collected and even then it still fails spectacularly when managing resources other than memory.
Users are not impressed by 5 page of meaningless stack traces while the app keeps on running pretending nothing happened.
You can make inroads on the exception problem with the use of high level / domain specific environments.. if the system is transactional or request/response oriented user code may never have to worry about problems and the environment is expected to just do the right thing ..these things break down in the general case.. as the system lacks necessary context.
In the end I think decoupling question of exception from the programming pardigram is a lost cause. If return codes and exceptions go away then the programming environment must be drastically different. You can only make it a better experience on the margins.
In C++, in addition to exceptions, boost::optional is useful:
Boost Optional
I have been coding for years, and been a mentor for a number of them also. I deal with very smart people and this is an area where less than enough time has been devoted to. Errors, exceptions, recovery and meaningful messages are not taught that much. Focus is on good algorithms and the hidden knowledge that your inputs from other programmers will be meaningful. Programming languages now are good enough, error returns ala c style are good enough. Programmers just have to be disciplined , You check inputs, range, type, length, whatever, and issue and error for things you are not expecting, which already happens for the most part, the part that is lacking is a defined interface where the the caller has a document that explains what the interface takes, and what it will return for success as well as error.
that comment did not once direct us to worship ron paul, nor did it tout the virtues of human slavery. clearly, you are not roman_mir, but rather an impostor who has taken over his account.
thank you.
Sounds like the author would like to have all their error-handling code nicely compartmentalized at the end of the function where it doesn't clutter the normal codepath, and I can't say I disagree, at least in many scenarios. So some techniques I'd like critiqued:
State-based exceptions - a way to do rough exception handling when any errors are unrecoverable and you're just trying to avoid making things worse. I've never done this, but now that I think of it there may be some potential there. As basic "local clean up" example
.... .... .... .... } // since we didn't actually fix the problem
UpdateSomething(...){
MyEnum state;
try{
state = OPENING
state = PROCESSING
state = SAVING
return;
}catch(...){
switch(state)
{
throw;
}
}
Another would be something like named catch blocks as "sub-functions", which, now that I RTFA, I think is similar to what the author was suggesting at the end. As I envision it being used as a c++ extension:
.... } .... } /* it's all error-handling below this point*/ .... } ...)// all other exceptions .... ; throw; } ...) .... }
Foo() {
try using catch_1 (watch_1)
{
try using catch_2 (watch_a, watch_b)
{
return;
catch_1 (watch_1, TypeA exception)
{
catch_1 (watch_1,
{
catch_2 ( watch_a, watch_b,
{
}
this would be functionally equivalent to the current situation, except that the code for catch statements would all be grouped after the end of the function rather than cluttering up the "all's well" codepath, and the variables accessible within the catch block would be declared up front to avoid scope obfuscation.
In fact this syntax could be extended to allow normal functions to be used as catch handlers as well, allowing common cleanup scenarios to be shared between multiple functions without requiring redundant wrapper code like catch(a,b,exc){cleanup_function_1(a,b,exc);}
--- Most topics have many sides worth arguing, allow me to take one opposite you.
The article boils down to "I can't keep track of the difference between normal execution code and error handling code therefore my solution is to put error handling code somewhere else so I don't have to look at it". The proposed "solution" just puts another layer of obfuscation into the code and can be done today with procedures. The proposal is not a step forward.
Were those links supposed to support your argument?
I'm sorry but they didn't explain anything at all.
... assertions and/or JUnit tests I would say. Then also use both exceptions and return values when and where necessary.
In Java (and C++ I believe?), runtime exceptions do not have to be caught. So you never have to do return values and/or exception catching for null pointers, bad arguments, etc. as long as you test adequately. The problem is that no one has the time and/or is able to test every scenario adequately. Also, in Java there is the UncaughtExceptionHandler, which again I believe is also in C++ (I haven't used C++ in a few years).
What we need is a fourth option, but we also need cars to run on water, a way to duplicate food, etc....
I think the key is really just good logging and testing. And as long as you have a dedicated team to QA, you'll be fine in most scenarios.
The G
Readability, no. Maintainability, sort of.
If an un-exceptional program is producing peculiar results, it can require considerable effort, often hours, to locate the cause of the oddity, and considerably longer to integrate the proper extensions to deal with it correctly.
An exceptional program, however, will produce an un-caught exception. Almost mechanically, I can insert catches at various points in the program, then hone in on where the proper catch should be applied. With virtually no understanding of the underlying program, I can, uh, repair it.
David Parnas pointed out how, in software, the efforts of a few can create need for the many. Nowhere is this more clearly evident than the rabid promotion of a fetish.
Exceptions, value propagation, panicing and ignoring all have merits and drawbacks; their applicability depends primarily upon the situation and scope.
The desire to ignore errors is so strong, that seeing how they are handled is considered a fault and a distraction from what matters.
As software becomes more complicated and more critical, focusing attention on errors, error conditions and error handling is what is required. Trying to make all this invisible is making things worse.
Surely there are different sorts of errors, which would suggest different approaches for dealing with them?
I guess it's pretty hard/futile to deal with most of these issues at a language level, because the appropriate course of action and channels of communication depend on the system. It strikes me that most of this stuff is something a domain-specific framework or API should be handling.
Exceptions should NOT be used for 'normal' errors
Exceptions should be used when you have an error that only your client code can resolve; I like to use writing to a full disk as an example. An I/O routine does not know what should be done if you try to write a full disk -- maybe your program cannot continue, maybe you can ask the user to delete some files. In most of today's programming languages, your error handling choices are:
There is, however, a third option that you have in Common Lisp but which could be implemented in any language: restarts. The thrower of an exception can and should be able to set "restart points," to which the catcher of the exception can transfer control flow after the error has been corrected. Thus, if you tried to write to a disk which is full, the catcher could ask the user to delete some files, then invoke the "try again" restart; the I/O routine knows how to retry the operation.
This requires an exception propagation mechanism that does not unwind the stack until some time after the exception is caught -- e.g. at the end of the exception handler. The exception handler's stack frame will need a pointer to the appropriate stack frame and return address for the case where no restarts are invoked (not hard), but otherwise the function call stack would be no different than it is now.
Palm trees and 8
When using a function, the current situation is that you get an out-of-band (the exception) or normal (typed return value) answer. This is a lie when you functionally look at it from the outside: you now have two types; the exception or the predefined typed answer. In case of an exception there is some glue which will give you a GOTO (to the catch block). The exception type is sometimes abused to return one of many types of answers from a function.
The only option is to lose the typiness (is that a word?). Some alternatives: Provide the function with handlers for certain categories of answers. Alternative 2: Use the type of answer to switch to a block of code given a certain type of answer (as exceptions are dealt with). Drawback is you can only give one return value (you might want to return multiple differently typed answers). Alternative 3: Return a type-prioritized set of answers; can be done already but is not 'best practice' at the moment. If a certain type of answer is not dealt with: return the function until it is done (a la exceptions).
A use case I have is that we send SMS batches but some messages may fail directly. Many types of errors may come back and all should be handled differently. We now make some plumbing with 'reply-object-classes' and they suck. They have to be created, filled and read...
Maybe combine a few possibilities. And yes: we are still in the pre-industrial area regarding software development. Everything takes expensive labour.
nosig today
A good example is the way Horn clauses present alternatives in Prolog -- although what you really want is not Prolog but something more like XSB with tabling and incremental table maintenance.
Seastead this.
.NET supports code contracts. This can make it clear to consumers of a component what input/output is excepted. Still throws exceptions (if configured to do so), but it is a step towards more clarity. Are there other languages out there supporting code contracts? I guess there must be - I've only used it in C# .NET.
10 LET the CODE be the DOCUMENTATION
20 LET A = A + 1
30 IF A = 11 THEN LET A = 12
40 IF A = 12 RETURN 2000
50 LET A = 9
60 RETURN 1000
The delayed execution is the 3rd option. Or an event model. Think of something like a make file where you don't do anything until all the pre-requisites have been satisfied. Error checking is necessary because ultimately you want to be able to have programs which can adjust to real world events (mouse motions, changing screen sizes, stuff arriving on the network wire). And in the real world thinks may not happen in the order you hope they would or in one of the orders you thought they could. Probably the most common way to handle this is publish subscribe or event model.
Any guest worker system is indistinguishable from indentured servitude.
Would this help? First, create unchecked exceptions to match each checked exception. Then, allow applications to somehow provide a list of checked exceptions that should be unchecked (which would carry the checked exception as a chained exception. Perhaps only checked exceptions returned by 'external' routines would be affected...
then you and anybody else with that signature will be marked troll.
Don't they teach about encapsulation to you youngsters these days?
The point is that library function A() failed to encapsulate some of the exceptions that B() can throw.
One)
For the return value option: It is usually bad coding practice to mix error codes with information the function returns. If a function returns an int, don't stuff an errno in there when things go wrong. Separate the data from the error code. I hate it when functions that are supposed to return a pointer use NULL as the error code. NULL doesn't tell you anything, and it very well may be a valid value to return anyway. Set errno, send a signal, return a separate error code, but don't put your noise (error code) in the signal.
Two)
I want to see the errors. Don't hide them from me. Force me to deal with them, at least as far as generating a meaningful error message. (Meaningful!. that's a different rant.) I don't want the hardware, OS, compiler, library or whatever to take away from me the ability to see -- and handle -- the error myself. Feel free to offer me some boilerplate error code, but don't force me to use it. Half of elegant programming is in the error handling. If you think about it, a program seems "smart" when it handles error conditions well, instead of just crashing or quitting.
And the plus:
It seems to me what the guy is complaining about is the aethestics of the code, not the actual error handling. Wouldn't a folding editor help with that? You just mark the error handling code and let the editor collapse it for you, until the time comes when you need to see it (and it will). Problem solved.
This is why we have testing.
Reading and understanding the manual and getting something right at the design stage takes less time than reimplementing it and retesting it after receiving feedback that the the initial private test has failed. And when private testing takes less time, you get to release the product to the public sooner and collect revenue sooner.
The only people who do it any other way are writing mission critical code that costs a fortune to develop. You know what? You're fired.
It appears you don't think your company's key product is critical to the mission of your company, sir. In that case, you can't fire me because I quit.
Sorry,
You only want to work in success state? The mark a of a truly good or great programmer is that the understand that faults happen and that they have to be dealt with. A great programmer makes this look simple and elegant.
An absolutely horrible programmer wants to hide this handling and do as little as possible with it. These programmers are also the ones that tend to somehow find a way to incorporate some new cool library or frame work into the code for no reason what so ever.
Unfortunately Agile is development models are inadvertently promoting this bad practice as people start to fee a need to bang out as many user stories as possible. Lots of shops create stories biased towards the success especially when stories are mostly penned by the business.
I call this problem. The success path code fallacy. The amount of code debt that results is huge.
Learn how to deal with errors early and efficiently so that you don't have to deal with them in unrelated bits of code where the amount of effort to deal with them is much much more. Then the amount of work you have to do with error checking will drop.
The problem with return codes, IMO, is that the operating system/runtime natively use exceptions, so when you *attempt* to convert code to using error codes, you have a false sense of security that your app won't crash due to thinking you're not using exceptions. I've run into this a couple of times: the architect thinks his/her app is rock-solid (in terms of crashing) but the fact is, you cannot escape exceptions. You may not throw exceptions, but your runtime does, and as a result, your code doesn't use try-catch anywhere. Then you get an exception you don't expect (i.e. a system exception, such as NullReference), and guess what? Your app crashes. Or even worse, you wind up putting a catch (Exception) {} into your code thinking you'll just convert your error into an error code, but you end up losing the true nature of the error as you convert your context-specific exception into a general "ActionFailed" error code. This kind of error winds up being a pain in the ass to diagnose, as you have to backtrack in your code to find where your error information got lost during the conversion.
And everything to do with the fact that error paths are rarely actually TESTED by the programmer, so if the program is complex enough the likelihood that an error condition will be handled properly rapidly drops to near zero.
The problem is thus really that of not being able to easily test that the error paths actually do the right thing.
-Matt
Make sure that EVERY error can be forced programatically and test all the error paths.
I can hear the screams of rage from here, but it's like backups, unless you test the restore process it may as well cp * /dev/nul.
Same thing with errors, unless you can test what happened when the error occurs you may as well not have error handling.
It's amazing the cruft that gets shaken out if you enforce this - particularly the "by the time the error gets printed the message is meaningless" ones.
Error handling built into the code is a fine way of creating thoroughly tested and fine tuned collection of spagettified bugs. Up to the final pre-delivery stages, the code should have no exception handling at all. This way, when sh!t happens, you don't have the luxury of deciding "this sh!t is unimportant, I will deal with it later". Since every bug blocks your progress, you have no choice but to find a root cause of every failure and fix it. Have somebody build a small circuit controlling a red beacon and a siren attached to the integration server and make it go off every time an automated unit test fails after a commit. Create an attitude where people are ashamed of setting off the integration failure alarm. That is the only way of getting somewhere close to a bug-free code that I know of.
This is a fallacy. The code shows how the problem is solved. The documentation describes the intent of the program - this is not the same thing. If the code is wrong the documentation describing the intent allows you to pick up the inconsistency, and what should actually be happening.
Yes, the documentation can also be wrong, and needs to be maintained as much as code and program data. It is part of our jobs to keep all the resources accurate and useful, including the documented *intent*. Anyone who says different is a lousy developer (most probably because they mistake the purpose of documentation, so under value it - or have never been given great documentation that saved them oodles of time).
I think he said that if a method uses a shared or outside resource and the method fails because the resource fails, then the method should send a return code. If a method might fail because the particular inputs are ill-conditioned, the programmer ought to code a method to check for the error condition before the method is invoked, and it is then the responsibility of the caller to use the pre-check to never make a call that will fail. This style worked OK in the old days, but nowadays just about everything depends on shared resources -- a database or network connection, a file system, or memory managed by the OS. So we are back to return code hell, because everyone has to send them.
Meyer's bottom line, however, is an interesting one. Exception handlers as as bug catchers don't really make much sense in production code. If you need to catch a bug, your program is wrong. Catching a error and patching things up on the fly so that no one notices is a pathological way to make wrong right.
Can you point me/us to good descriptions and/or discussions on this approach?
There's the problem.
Guns don't kill people; Physics kills people! - John Lithgow as Dick Solomon on Third Rock From The Sun
A common problem is that few development organizations take as much care in the design of their error handling as they do in the functional handling. Without this care, one result is that method signatures have too many caught exception declarations. This leads to poor exception handling in the caller and so on up the call stack. A good exception design -- for example Spring's -- leads to very few exception declarations on the method. I think that Spring's next step of hiding the exceptions is a mistake, but their code is far more used than mine so who am I to argue!
Absolutely not. Maybe there is a config file, and you just can't find it because it is somewhere else. Let the user know you can't find it and then offer a choice of browsing to the file or doing what you said. Nothing would be more confusing than thinking your config file is being read when in fact the software does what you said behind the scene instead. "Shit! No matter what option I change in the config file the damn thing just won't work!
Guns don't kill people; Physics kills people! - John Lithgow as Dick Solomon on Third Rock From The Sun
The fundamental problem is that sometimes an error is an error to the calling program, but sometimes it is not.
For example, when you issue: open "$HOME/.myconfig", the inability to find the file does not mean there is an error. Just that the optional config file is not there. But when you try to open the source file for an operation, the open-error really IS an error.
This duality happens at most levels. A library wrapping "open" will have the same problem. Does the caller consider this a fatal error or not?
Similarly, sometimes errors should result in telling the user and then quitting. But for a gui application it's better to show a graphical message and continue, even if the error is more or less "fatal".....
Mod parent up, I'm in complete agreement: "If there's a better way, show us. Come up with a solution. What's stopping you?" And indeed it's not obvious, or we would already have the solution.
I'm not sure I agree that it's possible to develop defect-free software. All hardware is unreliable. Mean time between failure.
Perfect software may be perfect in our minds; but software immediately degrades when implemented as machinery.
Perhaps the original poster is frustrated by the perfection in our minds failing to overcome the limitations of physical reality ... much as we all wish to live forever, even though we know that's not going to happen.
-kgj
Somebody please mod parent up to 11, and give them a knighthood, a late night television show, and a comedy nobel prize!
This needs to be read by so very many people!
You are seriously suggesting to install another whole package if your client is improperly configured ?? I suggest you quit any engineering activity if that was meant non-ironical.
Your mindset might be OK in the world of toy computing (read: Windows), but certainly not in the datacenter. Proper failure with proper error message is exactly what is required to built a robust solution. If I enter crap into a program it is supposed to properly complain, not to try an insane heuristic which will in 99% of cases do something I did not want it to do.
We can discuss all day what constitutes a proper error message (Windows gets it wrong half the time, see the event log), but there is not the slightest doubt we need them.
..bullcrap. Both SUN and to a lesser degree M$ never got a real handle on this huge boatload of code in the "standard" libs. Compare that to the POSIX API, where we have more or less 100% bug-free implementations.
If SUN had focused on providing a small, complete and orthogonal library with equivalent functionality as the POSIX API, I do think they would have been much more successful. Instead they loaded a large codebase on their shoulders and promptly sunk into a morass of bugs. In the end, it was just too much for their engineering resources.
In most cases, doing less is better than doing more, because you can focus on getting the smaller thing RIGHT. But hell, yeah, the software engineering profession is by now infested by all sorts of amateurs who have seen their "management talent" asking "Ohhh, does it also have an API for cooking coffee and one for booking meeting rooms ? I will only buy it if your language can do that !!!". As they are eager to please the management crappers and become crappers themselves one day, they are fascinated by the "comfort" functions of the Java/C# standard library.
All they get in reality is fast food. Tasty at first, feeling like a load of lead afterwards.
subj.
This isn't an either-or / where's-the-third option question - exceptions and return values just need to be used for what they're best suited.
Use exceptions for error conditions that can be generalized, i.e., they need no more information than this for a domain-specific (client/service/DAL) Policy to decide what to do.
1. Is it a genuine error, or a stopping condition encountered in normal operation?
2. Does it contain a message intended for the end-user to see?
3. Should it be logged?
No need for try/catch blocks everywhere for this since you've generalized it.
Error conditions that can't or shouldn't be integrated into a generalized exception-handling policy... these should relay information through function return values as part of an application's business process, not through exceptions.
It's not complicated.
What this guy is complaining about is that Error Handling code must reside at some point along the code that may encounter an error. He specifically mentions that he'd like to put all error handling in another file.
What he really wants is a tool for organizing code blocks in a more logical way.
What this guy wanta is something like the Leo editor. Of course Leo is not the only player in it's cathegory, see Outliners.
Now for the most interesting problem, singaling abnormal return conditions. Exceptions trully are the right contruct, specially for any code that is intended to be used in a larger program. Exceptions do the right thing and they do it better.
Code examples. For example, all these snippets of pseudocode are functionally the same:
# using out parameters
code = functionCall(argument1, argument2, OUT PARAMETER value)
if code != SUCCESS_CONST
return code
else
doSomething(value)
# using ad hoc structures
returnHandler = functionCall(argument1, argument2)
if not returnHandler.success
return returnHandler.errorCode
else
doSomething(returnHandler.value)
# using a resource object, basically same as above, but with more functions in the name space
resource = functionCall(argument1, argument2)
if not library_successTest(resource)
return library_getError(resource)
else
doSomething(library_getValue(resource))
# using exceptions
doSomething(functionCall(argument1, argument2))
Yes, the last example is exception oriented code. No Try/Catch needed. Must exception oriented code include a stupid amount of superfluous try/catch. The first idea is that code that doesn't handle errors shouldn't care about errors. in this example `doSomething` doesn't do anything with the error values of functionCall so why should it take so much effort to feed the return of functionCall to doSomething or bail?
If you argument is that the calling code shouldn't return an error and should handle the error; think about it. Whatever the parent intended to do has failed, so the parent still has to handle the error but now it doesn't have any information about what went wrong. Unless of course, you intend the caller to fix everything, in which case you live in a perfect world where errors don't occur. Or, you intend functions to handle errors for their all their calling parents which is insane.
But... the future refused to change.
I assert()....
The exception handler needs an exception handler...
Remember it is turtles all the way down.
Truth is stranger than fiction, but it is because Fiction is obliged to stick to possibilities; Truth isn't. Mark Twain.
Ever seen this?
try
{
"some code"
}
catch
{
}
A programmer should be fired for this. Basically, the programmer is 'hiding' a fault because he/she does not know how to deal with it.
This has been one of the worst programming constructs imaginable and don't say, "well so and so recommend we do it this". B#@@$$!~
I simply means that either they were too lazy to figure it out or did not give a d@$# about figuring it out or could not figure it out. Either way, it's a ticking time bomb in the code and will eventually cause problems down the road. This construct should be completely eliminated from exception handling by the compiler. i.e. Putting in an empty one should gen a compiler error such as 'Exception handling not defined.' And don't give me that BS about programmers choice. That's like saying let's go back to managing our own memory instead of using a garbage collector. The extremely few instances where one might justify something like this can be easily worked around so nobody should ever give the excuse that they had to do it this way.
I've been writing code for over 35 years and I have never had to implement this and, I never will.
There's a reason Valve is liked for releasing things when they're ready. People liked the same thing about Blizzard until Blizzard went apedung about maintaining its monopoly on matchmaking servers for its video games (the bnetd case).
If you have ever thrown an exception to indicate a success condition, you fail.
This is just plain wrong because if another developer ever implements an interface without a try...catch, you just caused the application to crash because of successful operation.
My experience with Exceptions, and API's that are full of them, is that the developers are pretty much saying they don't know what the hell is going on, so TRY everything and respond accordingly. There is this thing called Unit Testing, which means you build tests for both success and failing conditions for an interface and ensure the application will not crash and recover for failing conditions. An app should always succeed, so constantly trying interface calls is overhead. I only TRY calls to external API's because I cannot clearly know they will succeed, I didn't build unit testing in 3rd party tools therefore I don't trust them.
I mean, what the fuck does Exception stand for? Its an Exception to the known state of an app. If you throw for ALL known states including success, you fail.
I haven't thought of anything clever to put here, but then again most of you haven't either.
Thanks for the clarification. The nit I picked was out of scope, given the understanding that defect-free software assumes the integrity of the hardware.
-kgj
That's why they invented goto.
Confucius say, "Find worm in apple - bad. Find half a worm - worse."