I think that having alternatives to try/catch is generally worthwhile. Exceptions should only be thrown in exceptional circumstances.
So (to take some C# code as an example), I'd only call int.parse(myString) if it really, really should only get called when myString contains something that's parseable to an integer.
If it just probably contains an integer, then I'd call int.TryParse(myString, out returnedInt), and check the boolean return to see whether it was valid.
I think he has a really interesting POV but the conversation as a whole is definitely a very informative dialog on the value of try/catch in an async environment. As they say, read the whole thing...
I never read other people's code. In theory, you could use exceptions for nonunusual events... I've programmed a lot of things and I think I did that just once.
Here's how it is... operator overloading is really neat, but really only helps with matrix math and complex numbers, possibly more. It's only good when it's commonly understood what the operators do. To a noob, lots of things might be like algebra... in fact, only math is like math.
The problem with try/catch, really, is that it makes a tremendous amount of visual noise, which makes sense for critical operations that might crash everything, but just don't make sense when you can tolerate certain errors or checking for proper output with with a conditional looks better and is more appropiate.
Obviously then you have an issue with language conventions. How do you check for the returns of a method that does an in-place modification without returning anything? What happens when Null is a proper return value? What about having to access specific members or registers to check?
Perhaps, much like logging frameworks, we may need to categorize throwable actions by their importance, or think of a new convention that can handle such things more easily.
But what is your alternative? Doesn't checking the return code of every statement also make a tremendous amount of visual noise?
I've seen C code where about 80% of the code is dedicated to error handling and corner cases. Before every statement it has to check the current error status, and after it the return value... it's almost unreadable.
With try/except/finally a lot of that can be cleaned up, by handling the errors higher-up in the call hierarchy where they make sense to handle.
I see a lot of critique of exceptions, but haven't seen one better alternative yet, at least one that doesn't require switching to an obscure programming language.
Edit: this does assume that exceptions are used properly: for errors that should bubble up, not as extra return value.
This part really sunk in for me in Bruno Jouhier's reply:
> "So the big mistake I've always seen people make is being too "nervous" about exceptions and feeling that they have to do something about them as close as possible to the point where the exceptions were raised. They need to the exact opposite: feel relaxed about exceptions and let them bubble up."
Hadn't really thought about it in that way, but I find myself employing this pattern where possible - having said that, with async callbacks it can be hard to bubble up when you essentially have multiple logical processes occurring.
Haven't found a solution to this in my own codez yet but I feel a little twitch in my eye whenever I have to do:
try {
something = JSON.parse ...
because there's no native `JSON.validate` method.
Not complaining though, definitely not worth losing sleep over .. yet.
> "So the big mistake I've always seen people make is being too "nervous" about exceptions and feeling that they have to do something about them as close as possible to the point where the exceptions were raised. They need to the exact opposite: feel relaxed about exceptions and let them bubble up."
While the principle seems to make sense, I find it unusable with GUI like apps. GUI have hundreds of entry point from various events. If I want to catch exceptions far from their raise I need to do it in each one of those event handler, so that they don't bubble through librairies, literally hundreds of times.
Every beautifully written code I've seen has always been CLI programs. Exceptions probably works for server code where you can crash your single process and/or redo it from your original request. GUI and frameworks apps can't really use crash as an acceptable behavior.
>Every beautifully written code I've seen has always been CLI programs.
I might be a fairly novice coder, but I think a good solution for this would be a client/server model. I'm a full-time Linux user and for various reasons, I really like application which have a daemon mode, with the UI built as a client accessing said daemon. Good examples are mpd or deluge.
This way, the actual code can handle handle most, if not all exceptions and errors at the "CLI level" and expose meaningful error codes to the "API" that the UI uses. At least that's how I'm trying to build my first bigger software project (an image viewer).
Again, I'm a fairly inexperienced programmer, so this might be a foolish suggestion, but I try to give much thought to the way I'm building software. Maybe even too much, I'm kinda prone to over-engineering.
This is the standard in many languages and one that I believe that java got wrong. It was a little rushed with the whole concept of forcing everyone to handle all exceptions concept and building so many exceptions in for silly things like connection failures and parsing errors that reasonably can be expected to happen constantly in the normal runtime of an application.
I follow the rule in almost every language that my app should still be able to run normally if all try catches were remarked out, otherwise I should be handling something better. Exceptions are expensive and don't always back out nicely when they unwind in most languages.
In Objective-C, we play C rules more often then not and almost never use exceptions (except for assertion exceptions). Rather most errors have a ( out NSError ) pointer if they need to pass up errors.
In Python, it's more pythonic to ask for forgiveness than to check before hand so exceptions happen all the time. I'm not sure how I feel about that though but it seems to work well enough.
> It was a little rushed with the whole concept of forcing everyone to handle all exceptions
Just to clarify, Java doesn't force you to handle all exceptions (maybe it did at one point in time?). Exceptions which inherit from RuntimeException are unchecked and you can choose whether to handle them or not. Exceptions which don't inherit from RuntimeException are checked
That being said, I'd like to quote these passages from the official Java tutorial:
"Generally speaking, do not throw a RuntimeException or create a subclass of RuntimeException simply because you don't want to be bothered with specifying the exceptions your methods can throw.
Here's the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception."
Anyway... you don't need to neccessarily handle exceptions even if they are checked (in cases where it doesn't make sense that your code handles them). Add a 'throws' clause and let the calling code handle them.
So the poster of the grandparent comment really got it wrong when it comes to Java and exceptions.
> you don't need to neccessarily handle exceptions even if they are checked (in cases where it doesn't make sense that your code handles them). Add a 'throws' clause and let the calling code handle them.
The problem with this is it affects the signature of all methods in your call hierarchy up to the point where it's handled as you're forced to declare that you throw exceptions. A simple change in one class could quickly become a breaking change involving several classes just because an exception can now occur.
Ruby also gets it wrong in several common cases. The IO libraries throw exceptions on expected events, for instance, and there are a couple of places where I flat-out disagree with the Exception class hierarchy.
Anybody who wants to judge try/catch should first go and read about Common Lisp's condition system. See for example the chapter about conditions and restarts from the excellent book "Practical Common Lisp" by Peter Seibel:
Notice I'm not saying you should go and program in Common Lisp, just that you should understand those ideas before you engage in any sort of meaningful discussion about exceptions, errors and error handling in general. Because, you know, some things have already been thought about and invented. Don't re-invent them.
And on a more practical level, in languages with dynamic binding it isn't difficult to provide error handlers and "send" them up the call chain, so that in case of an exception your handler gets called, fixes the problem, and lets the called function continue. You can do all that using try/catch as a low-level tool, I've seen it done in Clojure, using JVM's exception system.
I work in Windows file systems for a living. We very much live with two models: exception handling and canonical return codes. I cannot tell you how many times I would have killed for Lisp-like conditions. If I were to tell you that, however, I would also have to tell you that most of the killing would have been in vain.
The trouble with the never-ending "try-catch versus conditions versus return codes versus fail-fast" argument is that there is no easy way to have the conversation about big swaths of code. The log parser is a great example, yes, but it is exceedingly simple. The fact is that 'low' in the linked example is exposing its guts to 'high' whether it likes it or not, and that in any reasonably large body of code this too can become unmanageable.
Wherever you see a religious war, your Spidey sense should be tingling, telling you: people are arguing over which tool is better for all jobs when in fact you might want to learn all of the tools and choose the best whenever possible. And that you will at times show up to a job site where they're using the wrong tool and you'll have to learn to change them or live with it -- whichever makes more sense/is more possible.
That said, sometimes consistency just wins out. A module that throws to a module that returns is always baffling, but if it's hiding this fact from the rest of the module -- or many more modules -- then it's worth it.
(edited to note that these are Windows file systems, hoping to avoid 'is that really so')
Inspired by Lisp's condition system I wrote a ruby library which comes pretty close to Lisp's error handling. It also implements "restarts" ... a little bit messy though since, as you said, low-level execption are used to get it working. In one point the condition system bootstraps itself which is quite interesting to think about.
In the beginning I thought it can only be ported to Ruby because I used some Ruby-specific features. The exception solution (which I implemented later) should work in a lot of different languages though.
Another thing is, if you understood the condition system, you can use it for a lot of different things besides error handling. You can build protocols on top of that, event handling etc. Freedom is yours.
Have you considered using Ruby's throw/catch instead of exceptions? I did that for Atomy's condition system[1]. They worked great because you can just use the restart names for the throw/catch tags.
I might have considered throw and catch as I wrote the library, but right now, I cannot think of any good reason why I haven't used it. Will check that again. Thanks for the suggestion.
Spot on, but CL's error generation/capture/recovery mechanism only helps in single-threaded code composition. Erlang's model of linking up processes so that a "supervisor" process get notified if a servant dies is the counterpart in a concurrent scenario. Together, they seem to me to cover most of the ground.
Furthermore, in Haskell you can throw an exception to another thread, though I'm not sure whether that's any more valuable as a design tool than a plain message passing channel. (see also "throwTo & block statements considered harmful" http://www.haskell.org/pipermail/haskell-cafe/2006-December/...)
Good point! I never actually considered this, because almost all Common Lisp code I wrote was single-threaded (CL and threads aren't friends), and nowadays I write in Clojure, where I just stick to fairly plain catch/throw.
This is a great example of why it is always worth it to learn various languages, not just stick to what you know. You regularly get eye-opening revelation moments.
"We believe that coupling exceptions to a control structure, as in the try-catch-finally idiom, results in convoluted code. It also tends to encourage programmers to label too many ordinary errors, such as failing to open a file, as exceptional.
Go takes a different approach. For plain error handling, Go's multi-value returns make it easy to report an error without overloading the return value. A canonical error type, coupled with Go's other features, makes error handling pleasant but quite different from that in other languages.
Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function's state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code."
This is a bad idea that keeps coming back again and again.
I see nothing wrong with exceptions, I do have a problem with (1) checked exceptions, and (2) catching exceptions prematurely and (3) people not learning how to use "finally" so they do (2) and rethrow.
Languages like Go and Scala roll out various mechanisms that bring us back to the bad old days of C, when we had to check the return/value and or the error code after every function call... if we wanted error handling to work.
The trouble with this approach is that it increases code bulk. For CS class projects, this isn't so bad, but when you're building real systems, the complexity of the error handling can approach or exceed the complexity of the "normal" path and when that happens you're in deep trouble.
Exceptions drastically reduce code bulk by introducing default "abort" behavior, which can itself be aborted at any level of the program and which can invoke cleanup anywhere in between.
Many programmers in many situations would be perfectly happy to catch "failure to open a file" and "failure to open a database connection" and "failure to connect to a network host" with one simple handler that logs the failure and either aborts, retries or ignores.
> when we had to check the return/value and or the error code after every function call... if we wanted error handling to work.
Errors as return values force you to think about every possible error, which is a good thing for code quality. Look at how much rock stable C software we have out there. Software that can be compiled on many different architectures, run in many different environments, and it all just works, even 20 years later.
Writing code with try/catch is much less work. Not because you have to do less typing, but because you simply think less about how errors should be handled.
At least in languages like Haskell you can use things like Monads that allow you to write the high level code and do the error routing for you behind the scenes. In C manually writing the error handling is a painful and error prone process.
And you know what? In my experience it's not less robust.
So my code hasn't had every possible failure case thought through and explicitly handled in advance. That's good. Firstly some of those errors are so rare they'll almost certainly never occur in my program's lifetime; by not having to handle them explicitly and individually I save time and money. Secondly I can guarantee that, no matter how good I think I am, the program will eventually find a way of crashing I'd not considered; this approach gives me a clean means of handling unforeseen errors as well.
A bad programmer can write bad code in any pattern and with any tools. A bad C programmer using return values can create so many different standards for how to handle errors that you might as well get out the divining rods to read the code. Personally, done right (as with anything) I happen to like try...catch.
No, I agree, but there's a cost/benefit calculation to be done. Plus it's not like the try...catch solution that doesn't explicitly handle the error will blow up and destroy everything; if the program is properly designed it should merely degrade onto the path of general handling, log the failure and halt whatever was being done.
BSD kernels have had numerous exploits over the years. GCC is a massive hairball. gzip has had exploits (http://www.kb.cert.org/vuls/id/381508 for one).
Without defending any given piece of software, the job of the exception advocate is not to prove that return values suck, but that using exceptions produces more stable software as fast or faster. This question cannot be resolved by examples/anecdotes alone.
In a modern programming environment you can't think about every possible error. In particular, code often migrates into distributed systems where a whole new range of problems can happen.
For instance, a system might have plug-ins that get data from a CSV file, a relational database and a web service. One day somebody comes along and adds a plug-in that gets data from a noSQL database.
Add a new component to the system and you introduce not only new failure modes but new ways failure impacts the "system a s a whole."
The more decisions you make, the more you will make wrong decisions. Exceptions provide a reasonable default behavior that is "decision free" and reasonable ways to upgrade it.
20 year old C software lived in a simpler world; only specialized network utilities like telnet and tcpwrappers would have to face the consequences of a failing DNS lookup or the temporarily failure of a network switch in Toledo.
It is a more complex world, but I think that's orthogonal to whether you prefer return codes or exceptions.
In fact, I think you can argue that keeping error-handling local to the call site (return codes) encapsulates and abstracts the errors better than letting an exception propagate arbitrarily far up the stack.
Exceptions are decision-free, but not making a decision (propagating an exception without handling it) doesn't make you any more robust to the vagaries of the modern world, it just moves the problem somewhere else.
Now, if multiple children in a call graph can experience the same error, and should be dealt with in exactly the same way, then propagating an exception up to a common ancestor in the call graph makes your code simpler. The fact that the compiler writes that dumb plumbing code for you is a great argument for exceptions, but not all applications fit the use case of:
* Same error can manifest itself in many places
* Each instance of the error can be dealt with in a similar-enough way to make a common exception handler simpler than handling errors at each call site.
Edit: Oh, did you mean that if you don't intend to handle an error, exceptions crash your program (good) rather than let it continue silently and do something you don't expect (bad)? If so, good point, and my apologies for the misunderstanding :)
I'm sorry, but this is a very bad example. iptables had an issue for a long time where error code is not carefully preserved in many situations and you end up with messages like:
iptables: Unknown error 4294967295
This wouldn't happen with exceptions - even if not handled properly, you'd see where is it originating and what's the most probable cause of the issue. And it's not necessarily iptable's fault - in some cases you have to really bend some rules to get the error you want. Also you cannot stack them so if you fail to cleanup after the original error, what do you return? The first or the second error? One has to be ignored.
Counterexample: you failed parsing some number correctly, but the backend where you log broken transactions is corrupted and cannot be written to. You'd rather drop the second information?
Ah, so you're saying that failing to parse the number breaks the transaction.
Fair enough, you'd rather (as a human being) have the second one. As a matter of building the system, I would still return the first one.
Basically, the second information is important in the sense that you really want an audit trail, but unimportant in that it doesn't tell you why your call failed. Returning it to the caller is useless compared to returning the first error.
But yes, it should certainly be logged and potentially acted on. An application can't do anything useful with the information, but an ops guy can.
This is not my experience at all. Return codes are far too easy to ignore. I would argue that the fact that C software works after 20 years is because it's been debugged for 20 years, everything that can possibly happen to it has probably happened and been handled. It's not because error return codes are a fundamentally better way to do this.
I know it's kind of unhip right now, but I would argue that what you're asking for is better handled using checked exceptions. That really forces you to think about error conditions, and it's enforced by the language. I'm continuously baffled by the argument that this is a bad idea.
How do you feel about Erlang's "let it fail" policy? I personally was afraid of it at first thinking you couldn't write stable code, but the result was quite the opposite. Things fail, and get started back up by supervisors and everything is happy. No error checking and code bulk, no try/catch nonsense littered all over the code.
> Many programmers in many situations would be perfectly happy to catch "failure to open a file" and "failure to open a database connection" and "failure to connect to a network host" with one simple handler that logs the failure and either aborts, retries or ignores.
aren't exception appropriate a according to a certain portion of developers. The reason they aren't exception appropriate is that you should expect these things to happen from time to time, ie they are not exceptional.
using exceptions to handle them is part of the problem Go attempts to solve by having multiple return values.
As far as I can tell either your point is a circular argument or it's an English-language nomenclature complaint fixable by s/exception/fooglewoo/.
Either way it doesn't address the real argument, about where it is appropriate to use try and catch. What is inherently better about multiple return values at every level, compared to semi-centralized catch blocks?
My point was that there is a school of htought that exceptions should only be used in "exceptional" circumstances.
The examples given were not exceptional in that a programmer should expect those types of errors during the normal execution of their program. Therefor exceptions aren't the solution to those types of errors.
But using that school of thought to support that school of thought isn't an argument, it's a circle.
PaulHoule is explaining why he likes a specific mechanism compared to another, and you are only replying with the previously-established fact that there exists a disagreement here, not a counterargument.
It's not really an objection to terminology. "Fooglewoo" handlers would still exist outside the normal flow of control while handling things that many people would consider routine.
Burying that kind of every-day, expected-circumstance logic in a side channel is, at least in some developers opinions, detrimental to the readability and understandability of a program.
I do have a problem with (1) checked exceptions, and (2) catching exceptions prematurely and (3) people not learning how to use "finally" so they do (2) and rethrow.
I am probably one of those people who catches exceptions prematurely and who hasn't learned to use "finally." If you link to some advice on how to use such things, I'll read it. I want to believe that I can learn a better way to use exceptions, but they just haven't clicked for me.
Then again, I've been using exceptions in an environment (C#) that matches the author's JSON.parse() example very well. The .NET library designers already decided what counts as exceptional, and it's often not possible for me, as a .NET user, to decide much of anything about the use or placement of try/catch.
...when you're building real systems, the complexity of the error handling can approach or exceed the complexity of the "normal" path...
I can understand all too well that a complex error handling path is intimidating, tedious, distasteful, and no fun at all. But in every serious project I've been a part of, error and exception handling has been where the bulk of the design, implementation, and testing work was done. That stuff is precisely how serious systems distinguish themselves from toys. Having a catch-all crash-path doesn't change that, because the serious system is not permitted to crash. As the author here points out, that's what assert() has offered for decades anyway.
>The .NET library designers already decided what counts as exceptional, and it's often not possible for me, as a .NET user, to decide much of anything about the use or placement of try/catch.
What do you mean? You can certainly decide what's exceptional. You can roll your own exceptions. You can catch and discard or handle exceptions you don't want to bubble up. You can put try-catch everywhere or nowhere (or choose a reasonable place in between). Your hands are not tied by .net exceptions any more than they are tied by any other reasonable error handling model.
I can decide to make something in my code exceptional, sure. I can't decide to make something in the library not-exceptional, though.
You can put try-catch everywhere or nowhere (or choose a reasonable place in between).
This isn't always the case. Sometimes the only way to answer a question ("Can this string be parsed as an integer?") is to try it and catch the exception. And there's just no way to do that coherently without catching it immediately.
Later releases of .NET do include a non-exception-throwing TryParse() call in many places. I'm pretty sure I've run into some cases where that was not available, however, and I'm pretty sure I've run into similar situations in other methods besides Parse(). And TryParse() was a late addition; look back in the 1.1 or 2.0 docs, and it doesn't exist.
In other words, somebody thought it was reasonable to force .NET users to catch some exceptions immediately.
Your hands are not tied by .net exceptions...
No, but there's not much point in using .NET if I'm not using the library that comes with it. And the design of the library does tie my hands in some cases.
I think the author makes a great point about libraries that use exception handling blurring the line between bugs and expected problems. That's exactly how I feel about the C# work I've done.
> I can decide to make something in my code exceptional, sure. I can't decide to make something in the library not-exceptional, though.
Making something in the library non-exceptional is equivalent to discarding an error. Catch the exception and discard it. Done. Do this at whatever level you feel is appropriate (or don't, and handle the exception in a more reasonable fashion).
> In other words, somebody thought it was reasonable to force .NET users to catch some exceptions immediately.
What do you suppose should be done? The other option seems to be to continue in a erroneous state. I'm probably not familiar with every possible error-handling methodology, but it seems the main ones are "error codes" (those worked so great in C, right), "exceptions" (annoying, but error handling in general is annoying), and "injected handling" (where the caller can inject error handling code somehow; but this is more complicated and requires deeper knowledge about the callee). Is there a better option?
> No, but there's not much point in using .NET if I'm not using the library that comes with it. And the design of the library does tie my hands in some cases.
My point is that you are not tied by .Net any more than you are tied by any other exception-handling language. You can handle exceptions where and how you feel is appropriate.
> I think the author makes a great point about libraries that use exception handling blurring the line between bugs and expected problems. That's exactly how I feel about the C# work I've done.
I think this is a bit of a red herring. An exceptional condition is simply a special case. A bug in the code is an exceptional condition. A problem parsing a number is an exceptional condition. Modern languages generally have specific exceptions to allow you to handle different situations with custom logic, but it's important to note that the runtime can't reliably distinguish between "bugs" and "expected problems". Did your number parsing fail because the user entered an invalid value or because your code grabbed column 3 instead of 4?
Making something in the library non-exceptional is equivalent to discarding an error ... The other option seems to be to continue in a erroneous state.
I guess this is where we differ. I feel that the designers of the .NET library have chosen to throw exceptions in places where nothing exceptional is actually happening, where no error has occurred, where the programmer may very well be expecting the "exceptional" outcome.
Where my code must handle such conditions, forcing me to handle them as exceptions makes my code longer, less readable, harder to change, and harder to reason about.
...it's important to note that the runtime can't reliably distinguish between "bugs" and "expected problems".
This is exactly what the author of the linked piece points out, this is a part of my complaint, and it's an issue Microsoft has tacitly acknowledged the seriousness of by the addition of alternatives to exception-throwing calls, like TryParse().
The problem from my perspective isn't the runtime or the languages that target it, but choices made when the library was designed.
It does not feel to me, as a user of these massive libraries, that there was any systematic way of deciding what should be and what should not be reported as an exception.
> I guess this is where we differ. I feel that the designers of the .NET library have chosen to throw exceptions in places where nothing exceptional is actually happening, where no error has occurred, where the programmer may very well be expecting the "exceptional" outcome.
When does this actually happen? Are you really expecting it to fail when you open a file, or when you parse an integer, or whatever else? Where is .Net throwing exceptions in cases that no error has occurred and that you expect?
This complaint is common, but it feels rather hollow to me. Most of the time it seems to come down to a preference for error codes over exceptions, or an annoyance with the try-catch boilerplate, rather than a legitimate complaint about exceptions being thrown inappropriately.
> Where my code must handle such conditions, forcing me to handle them as exceptions makes my code longer, less readable, harder to change, and harder to reason about.
Are you suggesting that every function should have two versions like Parse and TryParse? Is this really what you'd prefer the .Net team work on, instead of providing new tools and functionality? Or are you wanting something like "ON ERROR RESUME NEXT" so that you can ignore these "expected" errors?
> This is exactly what the author of the linked piece points out, this is a part of my complaint, and it's an issue Microsoft has tacitly acknowledged the seriousness of by the addition of alternatives to exception-throwing calls, like TryParse().
Eh, the linked piece seemed mostly to be pining for the days of error codes. There's no general way to determine if a failure is a "bug" or "expected", not for exceptions and not for anything else. If you want to avoid exceptions for "expected" failures, then you're asking for no exceptions at all, which is fine, but the problem isn't just the definition of "exceptional".
> It does not feel to me, as a user of these massive libraries, that there was any systematic way of deciding what should be and what should not be reported as an exception.
The systematic way was "it's exceptional if it's not the desired or expected outcome". The addition of TryParse was a nice bonus, but is in itself an exception to the exception model.
Can you give me a practical situation where an exception is thrown despite there not being an error, aside from the canonical Integer.Parse() example? In my experience, that's not the bulk of any practical program, and it's still exceptional from the point of view of the Parse function.
I'd be interested in discussing this, but I'm not really sure what you think would be an improvement. And yes, it's a rather uphill battle if your proposal is to use error codes most of the time. I think that ship already sailed (although there's a strong case for error codes in C++, but that's kind of a special case).
However, in the interest of trying to be helpful, my guess is that you have a bug in your app that plays poorly with a bug in Win32. A quick search for BufferedGraphicsContext.CreateCompatibleDIB yielded this question on SO (link to top answer) which indicates a resource leak may be at fault:
Languages like Go and Scala roll out various mechanisms that bring us back to the bad old days of C, when we had to check the return/value and or the error code after every function call... if we wanted error handling to work.
I cant speak for Go but Scala's Option type is very different from C error codes since it is a monad. As a result you can use it with Scala's 'for' comprehensions (which are roughly the same as Haskell's arrows) and write code that looks roughly imperative without having to explicitly handle any errors.
when you're building real systems, the complexity of the error handling can approach or exceed the complexity of the "normal" path and when that happens you're in deep trouble.
Go is a systems programming language, and a large part of systems programming is dealing with the potential errors. Take a look at, say, how the Linux kernel implements a system call. In fact, this is do_mmap_pgoff(), which does the bulk of the work for a mmap() system call in Linux: http://lxr.linux.no/linux+v3.1.1/mm/mmap.c#L942
It's almost nothing but error checking, and I submit that is as intended. In this circumstance, you want all of the error checking right in front of you, because that error checking is enforcing very important kernel policy. A lot of kernel code is error checking, because it has a lot of policy to enforce.
You're right that this error checking paradigm is a throwback to C, but Go was designed as a better C. Go's means of handling errors is exactly what I wish I could do in C; it allows every function to both return a value and an error code. It avoids passing in pointer to values because the function returns an error code, or having to check a global errno because the function returns a meaningful value. It's kinda like living in a world where C++, Java and Objective-C were never invented. (I like and use C++, so please don't take that as jumping on the C++-is-the-worst-thing-ever bandwagon.) I find that a very interesting direction, one which should be explored.
I use exceptions for higher-level code. When writing, say, a parser, I'd rather throw and catch exceptions. I don't think we need to choose one error-catching paradigm and declare it's best for all levels of code.
Functions call panic() (via t.errorf) to avoid passing errors between multiple levels of functions, but then Parse method catches it with recover(), checks what kind of error it is, and either panics again if it's a runtime error, or returns other kinds of errors.
I don't think this style is much better than exceptions. I think Go could have done itself a favor if it has variants + pattern matching. I find those to be, in many cases, superior to exceptions as the compiler checks you are handling everything and you can easily encode success and failure in the variant type.
For some context here's another error handler in Google Go:
defer func() {
if r := recover(); r != nil {
if err, ok := r.(runtime.Error); ok {
if err.String() == "runtime error: index out of range" {
// handle bad index
return
}
}
panic(r)
}
}()
vs
try {
}
catch (IndexOutOfBoundsException ex) {
// handle bad index
}
A language where you have to resort to string compare to handle invalid array accesses can't be use as a model for good error handling IMO.
Of course that's just the tip of an iceberg with no way for IDEs/tools to know what return is an error, no way to know at a glance if "_" was an ignored error or ignored other extra return value, plus other fundamental problems caused by implicit types.
Whose crazy code is that? NOT idiomatic. If you need to recover from bad slice indexes then your code is broken and you have way more to worry about than a messy recover closure.
> If you need to recover from bad slice indexes then your [error handler] code is broken
You bring up a good point that when resorting to value comparisons for error handling instead of type comparisons it's easy to make mistakes. The code is as far as I can tell a simplest way to handle an index out of bounds, but to also handle a 'slice out of bounds' it would need to also compare to the string value "runtime error: slice bounds out of range" -- not helping the case for error handling in Google Go.
This code strikingly shows deficiencies in Google Go non-local error handling:
- Tons of boilerplate (defer, recover, re-panic, type check, value check)
- Not scoped so can only handle an error once per function
- Have to do value tests in addition since types are very generic due to implicit interfaces
- Error values are poorly defined
- Result for higher-level caller is buried deep in the function and non-obvious
These problems extend to all non-local error handling in Google Go, not just for this specific case of array and string indexes. That the idiomatic way to 'handle' an error is to abort the program is another separate problem.
"On Error Resume Next" does not do what you think it does. It ignores the error, and marches blithely on. That is very different than "continuing where you left off, once the error is handled."
"The really nice thing about the cb(error, result) approach is that it constantly reminds the programmer that you must acknowledge that failure may come from any request you make."
try/catch, which blurs the line between errors
that are *mistakes* (accessing a property of null,
calling .write() on a stream after .end(), etc.),
and those which are expected application-level problems
(invalid data, file missing, and so on).
In a typed exception system, you just assign different kinds of exceptions to all of those. Java also distinguishes between checked exceptions ("known unknowns") which need to be declared, and runtime exceptions ("unknown unknowns"). Sure, you can still catch all of them in one place, but that's very rarely a good idea.
A non-typed exception system (that doesn't solve the problem with another mechanism) just sounds like a very bad idea. Is Javascript like that?
It totally depends on your Application. I wouldn't call a non existing file referenced by a db record, an strange log entry or a malformed xml returned by a webservice an "expected application-level problem". There is absolutely no way for a library writer to decide between an expected and an exceptional condition.
At my (admittedly meager) 1024x768, between browser and now the google groups redesign, ui area takes up almost half of the upper space before any content is shown.
OMG. That is a horrible UI. It makes Groups totally unusable. Half of my laptop vertical screen is the unscrollable fixed panel with a few big buttons and the search bar. Then 1/4 of the horizontal screen is fixed with the left navigation links. The actual content are crammed on the lower right half of the screen. The bottom arrow of the scrollbar is missing. Please Google, not everyone has 24-inch vertical monitor.
It used to be that Microsoft's website took up lots of fixed upper screen real estate for "branding." Now they have fixed it and is much more usable. Have the Microsoft designers gone to work for Google now?
> At least JavaScript doesn't have typed catches. Holy black mother of darkness, that shit is intolerable.
I would like the author to expand a bit instead. In python there are typed catches, it seems to make a lot of sense to me: you catch only the exceptions you want, and let the other ones bubble up. It is well explained in Martelli's Python in a Nutshell.
I have seen try/catch construct in Java and Javascript, and it is probably less readable, and certainly can be over-used, but in Python, returning None in all exceptional cases is annoying, different issues got merged into a single "Muted" case.
Try/catch may be an anti-pattern in a dynamically-typed language, but not in a static-typed language. In static languages they're important and valid because they help avoid the "returned a null what?" question.
foo = JSON.parse(input)
if (!foo) return "invalid data
This is like saying that walking is faster then driving because I can walk 5 meters faster then it takes me to get into a car. Yes, error codes are more compact in a tiny "Hello world" example because it is only showing one function call. Exception handling becomes more compact when you're writing something less trivial and you don't have to repeat the same error handling code after every call.
> Try/catch is goto wrapped in pretty braces. There's no way to continue where you left off, once the error is handled.
Don't throw exceptions if you can handle the error and continue where you left off. Exceptions is for when you can't continue. Think of throwing as a way to roll back transaction, stop whatever you were trying to do, and go back to the last consistent state.
Obviously, exceptions are not perfect. As author correctly notes, they require careful consideration of what's exception and what's part of normal flow. But they were invented for a reason, and I don't see the article offering any alternative solutions to the problems that exceptions are solving now.
> Don't throw exceptions if you can handle the error and continue where you left off. Exceptions is for when you can't continue.
Yes, although quoting that example doesn't support the assertion. JSON.parse is a library function. How can it judge whether the caller can continue or not just because the JSON cannot be parsed?
> Think of throwing as a way to roll back transaction, stop whatever you were trying to do, and go back to the last consistent state.
Any try block that is larger than one atomic operation can become a nightmare to roll back, since the catch gives you no idea how far it was into the block, what resources were allocated, etc. So although try/catch avoids the hassle of checking state after each operation, you pay for it on errors.
> JSON.parse is a library function. How can it judge whether the caller can continue or not just because the JSON cannot be parsed?
Don't make assumptions about the caller, throw if your library can't continue.
> So although try/catch avoids the hassle of checking state after each operation, you pay for it on errors.
If your language supports RAII (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initial...) you get a free ride. Otherwise you have to write non-trivial cleanup code regardless of how you return errors. Try/catch (and especially finally) helps a lot because you get to group all your cleanup in one place. With error codes the code looks something like:
a = acquire(A)
if (!a) return
b = acquire(B)
if (!b) {
release(a)
return
}
c = acquire(C)
if (!c) {
release(a)
release(b)
return
}
...
This is a maintenance nightmare. It's hard to see what the code is doing because the real logic is hidden between piles of error handling stuff. If you add a new resource acquisition in the middle you have to go over every statement that follows and modify error handlers. And don't you dare changing the order of statements, because that basically forces you to rewrite the whole function. Believe me, you don't want this in your code, exceptions are your friend :)
No, the way to handle that pattern is with nested gotos:
a = acquire(A);
if (!a) goto err_a;
b = acquire(B);
if (!b) goto err_b;
c = acquire(C);
if (!c) goto err_c;
do_stuff(a,b,c);
err_c:
release(b);
err_b:
release(a);
err_a:
return;
This is precisely why goto is not universally evil.
> At least JavaScript doesn't have typed catches. Holy black mother of darkness, that shit is intolerable.
Well, if his complaint is about untyped catches, well, I guess they must suck. But if he thinks that typed catches are somehow worse (hint: they're better), he is wrong.
> But still, nothing is as bad as the common "On Error Resume Next" that so many terrible VB programs
start with.
Ok, everyone who always checks the return value of printf() in C, raise your hands. That's what I thought. The semantics of C is that whenever something fails, you just continue from the next line (statement, whatever...) even if the world is burning. Try-catch may suck, but checking for return values sucks more (in languages without pattern-matching), deal with it.
try/catch, which blurs the line between errors that are mistakes (accessing a property of null, calling .write() on a stream after .end(), etc.), and those which are expected application-level problems (invalid data, file missing, and so on).
While I tend not to be a big fan of try/catch myself, one thing I like about Objective C and Cocoa is that they at least provide classes to separate programmer mistakes (NSException, used with try/catch) from application issues (NSError, used however it best fits the app). If the try/catch pattern must be used, I think its sensible to have this kind of separation of responsibilities.
This reminds me of the Qt framework which is C++ but doesn't make any use of exceptions.
Before I worked with Qt, I never thought this was possible without sacrificing the API, but the Qt API is very clean and doesn't seem to suffer from that design decision. So I looked around in the Qt API for some time, trying to learn how they managed to get along without exceptions. And I think I finally got it.
The whole error handling in Qt mostly boils down to providing sensible null objects. That is, instead of using NULL pointers (as in C) or generic null objects (as in JavaScript), for all kinds of objects there are specialized null objects which behave sensible to the most possible extent.
I think the best is a little of both ways. For instance in python:
x = some_dict['meh']
Will raise if 'meh' doesn't exist. If you believe that 'meh' should be there in your program, it's fine to 'let it raise an exception'.
However, if 'meh' could be there, it's better to use an error style such as:
x = some_dict.getDefault('meh', 'some-neutral-value')
And continue without raising because there's no need to raise as there's nothing exceptional here.
Ideally, raising an exception should have the meaning "Hey, something is wrong here and I don't know what to do next". And someone in the call hierarchy would handle this and say "Oh, the connection stopped.. that's why it's not working. I'll reconnect and call you again".
And note that this "parent handling" might be way higher than where the problem occurred..
No he doesn't. defaultdict provides a default value for ALL missing values. His approach allows him to target one key in particular, and is a very common python idiom.
Note to everyone: don't confuse 'try-catch sucks' with 'exceptions suck'.
There's no inherent reason an exception-throwing method can't be invoked in a style that gives back a value/exception pair. That limitation is a property of the specific language, not all languages. For example: exceptions could be caught succinctly if method invocations prefixed with 'catch' returned a value|exception tuple (`v, ex = catch ParseInt("test")`).
The primary difference between exceptions and error codes is the default behavior. Error codes default to 'ignore' and exceptions default to 'propagate'. The rest of the differences (requiring acknowledgement, succinctness of handling, availability of stack traces) are more of a coincidence based on the common decisions in languages like Java, Go, C, C++, python, etc.
I think the main downside of existing exception implementations is they discourage programmers from defining useful error cases for functions. It's so easy to propagate or suppress that anything else feels like massive busy work. Simple improvements would be to make wrapping exceptions easy and defining new types easy (throws FileNotFound as forge MissingNetConfigFile).
On the other hand, the main downside of error code implementations is the ease of ignoring. Go has a very good idea in the "x, _ = funcWithIgnoredError" style, but it extends poorly to otherwise-void methods like 'flush stream'.
Isaacs is spot-on, and this is one reason why I love Objective-C. NSException is for programming errors, which should be rare. You can use try/catch, but you almost never do. NSError is for expected application-level errors.
This is exactly what I came here to say. NSException and NSError combined with the fact that you can send messages to nil objects and just get nil objects back lead to much "cleaner" code in my opinion.
The best part about exceptions (unchecked), is that they allow an exception to automatically bubble upward to the level of code that actually should be responsible for taking action, without having to write a ton of boilerplate code all the way down the call chain.
Even better, when stack traces in exception logs are examined, it's easy to see where in the call chain things are going wrong.
During a database transaction for example, something might go wrong 5 method calls deep, and I should roll back the transaction.
If I'm forced to either use checked exceptions or use if statements to check whether there was success at each level, then that's a lot of repeated boilerplate try/catch/rethrow or if(success) code which unchecked exceptions free me up from having to write.
If all I receive at the transaction level is a failure code, then what do I log, other than "something went wrong?!". With exceptions, all the work is done for me. I just log the exception stack trace and I can easily see what needs to be fixed.
Much of the time, exceptions are useful because they are thrown becuase of something you didn't anticipate happening, as opposed to something you had planned for when writing the code. And when that unanticipated thing does happen, there is nothing more beautiful than an automatically generated stack trace telling you exactly what happened.
139 comments
[ 3.1 ms ] story [ 181 ms ] threadSo (to take some C# code as an example), I'd only call int.parse(myString) if it really, really should only get called when myString contains something that's parseable to an integer.
If it just probably contains an integer, then I'd call int.TryParse(myString, out returnedInt), and check the boolean return to see whether it was valid.
Here's how it is... operator overloading is really neat, but really only helps with matrix math and complex numbers, possibly more. It's only good when it's commonly understood what the operators do. To a noob, lots of things might be like algebra... in fact, only math is like math.
Obviously then you have an issue with language conventions. How do you check for the returns of a method that does an in-place modification without returning anything? What happens when Null is a proper return value? What about having to access specific members or registers to check?
Perhaps, much like logging frameworks, we may need to categorize throwable actions by their importance, or think of a new convention that can handle such things more easily.
I've seen C code where about 80% of the code is dedicated to error handling and corner cases. Before every statement it has to check the current error status, and after it the return value... it's almost unreadable.
With try/except/finally a lot of that can be cleaned up, by handling the errors higher-up in the call hierarchy where they make sense to handle.
I see a lot of critique of exceptions, but haven't seen one better alternative yet, at least one that doesn't require switching to an obscure programming language.
Edit: this does assume that exceptions are used properly: for errors that should bubble up, not as extra return value.
> "So the big mistake I've always seen people make is being too "nervous" about exceptions and feeling that they have to do something about them as close as possible to the point where the exceptions were raised. They need to the exact opposite: feel relaxed about exceptions and let them bubble up."
Hadn't really thought about it in that way, but I find myself employing this pattern where possible - having said that, with async callbacks it can be hard to bubble up when you essentially have multiple logical processes occurring.
Haven't found a solution to this in my own codez yet but I feel a little twitch in my eye whenever I have to do:
because there's no native `JSON.validate` method.Not complaining though, definitely not worth losing sleep over .. yet.
While the principle seems to make sense, I find it unusable with GUI like apps. GUI have hundreds of entry point from various events. If I want to catch exceptions far from their raise I need to do it in each one of those event handler, so that they don't bubble through librairies, literally hundreds of times.
Every beautifully written code I've seen has always been CLI programs. Exceptions probably works for server code where you can crash your single process and/or redo it from your original request. GUI and frameworks apps can't really use crash as an acceptable behavior.
I might be a fairly novice coder, but I think a good solution for this would be a client/server model. I'm a full-time Linux user and for various reasons, I really like application which have a daemon mode, with the UI built as a client accessing said daemon. Good examples are mpd or deluge.
This way, the actual code can handle handle most, if not all exceptions and errors at the "CLI level" and expose meaningful error codes to the "API" that the UI uses. At least that's how I'm trying to build my first bigger software project (an image viewer).
Again, I'm a fairly inexperienced programmer, so this might be a foolish suggestion, but I try to give much thought to the way I'm building software. Maybe even too much, I'm kinda prone to over-engineering.
I follow the rule in almost every language that my app should still be able to run normally if all try catches were remarked out, otherwise I should be handling something better. Exceptions are expensive and don't always back out nicely when they unwind in most languages.
In Objective-C, we play C rules more often then not and almost never use exceptions (except for assertion exceptions). Rather most errors have a ( out NSError ) pointer if they need to pass up errors.
In Python, it's more pythonic to ask for forgiveness than to check before hand so exceptions happen all the time. I'm not sure how I feel about that though but it seems to work well enough.
Just to clarify, Java doesn't force you to handle all exceptions (maybe it did at one point in time?). Exceptions which inherit from RuntimeException are unchecked and you can choose whether to handle them or not. Exceptions which don't inherit from RuntimeException are checked
"Generally speaking, do not throw a RuntimeException or create a subclass of RuntimeException simply because you don't want to be bothered with specifying the exceptions your methods can throw.
Here's the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception."
Anyway... you don't need to neccessarily handle exceptions even if they are checked (in cases where it doesn't make sense that your code handles them). Add a 'throws' clause and let the calling code handle them.
So the poster of the grandparent comment really got it wrong when it comes to Java and exceptions.
The problem with this is it affects the signature of all methods in your call hierarchy up to the point where it's handled as you're forced to declare that you throw exceptions. A simple change in one class could quickly become a breaking change involving several classes just because an exception can now occur.
http://www.gigamonkeys.com/book/beyond-exception-handling-co...
Notice I'm not saying you should go and program in Common Lisp, just that you should understand those ideas before you engage in any sort of meaningful discussion about exceptions, errors and error handling in general. Because, you know, some things have already been thought about and invented. Don't re-invent them.
And on a more practical level, in languages with dynamic binding it isn't difficult to provide error handlers and "send" them up the call chain, so that in case of an exception your handler gets called, fixes the problem, and lets the called function continue. You can do all that using try/catch as a low-level tool, I've seen it done in Clojure, using JVM's exception system.
The trouble with the never-ending "try-catch versus conditions versus return codes versus fail-fast" argument is that there is no easy way to have the conversation about big swaths of code. The log parser is a great example, yes, but it is exceedingly simple. The fact is that 'low' in the linked example is exposing its guts to 'high' whether it likes it or not, and that in any reasonably large body of code this too can become unmanageable.
Wherever you see a religious war, your Spidey sense should be tingling, telling you: people are arguing over which tool is better for all jobs when in fact you might want to learn all of the tools and choose the best whenever possible. And that you will at times show up to a job site where they're using the wrong tool and you'll have to learn to change them or live with it -- whichever makes more sense/is more possible.
That said, sometimes consistency just wins out. A module that throws to a module that returns is always baffling, but if it's hiding this fact from the rest of the module -- or many more modules -- then it's worth it.
(edited to note that these are Windows file systems, hoping to avoid 'is that really so')
https://github.com/melkon/conditions
An example ("parse_log_file" and "log_analyzer show restarts"): https://github.com/melkon/conditions/blob/master/example/exa...
In the beginning I thought it can only be ported to Ruby because I used some Ruby-specific features. The exception solution (which I implemented later) should work in a lot of different languages though.
Another thing is, if you understood the condition system, you can use it for a lot of different things besides error handling. You can build protocols on top of that, event handling etc. Freedom is yours.
[1]: https://github.com/vito/atomy/blob/master/kernel/condition.a...
Furthermore, in Haskell you can throw an exception to another thread, though I'm not sure whether that's any more valuable as a design tool than a plain message passing channel. (see also "throwTo & block statements considered harmful" http://www.haskell.org/pipermail/haskell-cafe/2006-December/...)
Go's "defer" is nice syntax btw.
This is a great example of why it is always worth it to learn various languages, not just stick to what you know. You regularly get eye-opening revelation moments.
"We believe that coupling exceptions to a control structure, as in the try-catch-finally idiom, results in convoluted code. It also tends to encourage programmers to label too many ordinary errors, such as failing to open a file, as exceptional.
Go takes a different approach. For plain error handling, Go's multi-value returns make it easy to report an error without overloading the return value. A canonical error type, coupled with Go's other features, makes error handling pleasant but quite different from that in other languages.
Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function's state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code."
http://golang.org/doc/go_faq.html#exceptions
In Go, if there's a programmer error, call panic(); if there's a non-programmer error, return it as a second return value.
[Added] Plus, it's obvious to see where people ignore errors:
"_" is a throw-away variable to indicate that this value won't be used in the code. It's obvious that the programmer decided to ignore the error. If you don't use "err" later, this won't compile. This code handles error.I see nothing wrong with exceptions, I do have a problem with (1) checked exceptions, and (2) catching exceptions prematurely and (3) people not learning how to use "finally" so they do (2) and rethrow.
Languages like Go and Scala roll out various mechanisms that bring us back to the bad old days of C, when we had to check the return/value and or the error code after every function call... if we wanted error handling to work.
The trouble with this approach is that it increases code bulk. For CS class projects, this isn't so bad, but when you're building real systems, the complexity of the error handling can approach or exceed the complexity of the "normal" path and when that happens you're in deep trouble.
Exceptions drastically reduce code bulk by introducing default "abort" behavior, which can itself be aborted at any level of the program and which can invoke cleanup anywhere in between.
Many programmers in many situations would be perfectly happy to catch "failure to open a file" and "failure to open a database connection" and "failure to connect to a network host" with one simple handler that logs the failure and either aborts, retries or ignores.
Errors as return values force you to think about every possible error, which is a good thing for code quality. Look at how much rock stable C software we have out there. Software that can be compiled on many different architectures, run in many different environments, and it all just works, even 20 years later.
Writing code with try/catch is much less work. Not because you have to do less typing, but because you simply think less about how errors should be handled.
So my code hasn't had every possible failure case thought through and explicitly handled in advance. That's good. Firstly some of those errors are so rare they'll almost certainly never occur in my program's lifetime; by not having to handle them explicitly and individually I save time and money. Secondly I can guarantee that, no matter how good I think I am, the program will eventually find a way of crashing I'd not considered; this approach gives me a clean means of handling unforeseen errors as well.
A bad programmer can write bad code in any pattern and with any tools. A bad C programmer using return values can create so many different standards for how to handle errors that you might as well get out the divining rods to read the code. Personally, done right (as with anything) I happen to like try...catch.
Which would be what exactly. The work of Knuth and djb I'll grant you, but the rest?
Care to try again?
For instance, a system might have plug-ins that get data from a CSV file, a relational database and a web service. One day somebody comes along and adds a plug-in that gets data from a noSQL database.
Add a new component to the system and you introduce not only new failure modes but new ways failure impacts the "system a s a whole."
The more decisions you make, the more you will make wrong decisions. Exceptions provide a reasonable default behavior that is "decision free" and reasonable ways to upgrade it.
20 year old C software lived in a simpler world; only specialized network utilities like telnet and tcpwrappers would have to face the consequences of a failing DNS lookup or the temporarily failure of a network switch in Toledo.
In fact, I think you can argue that keeping error-handling local to the call site (return codes) encapsulates and abstracts the errors better than letting an exception propagate arbitrarily far up the stack.
Exceptions are decision-free, but not making a decision (propagating an exception without handling it) doesn't make you any more robust to the vagaries of the modern world, it just moves the problem somewhere else.
Now, if multiple children in a call graph can experience the same error, and should be dealt with in exactly the same way, then propagating an exception up to a common ancestor in the call graph makes your code simpler. The fact that the compiler writes that dumb plumbing code for you is a great argument for exceptions, but not all applications fit the use case of:
Edit: Oh, did you mean that if you don't intend to handle an error, exceptions crash your program (good) rather than let it continue silently and do something you don't expect (bad)? If so, good point, and my apologies for the misunderstanding :)Fair enough, you'd rather (as a human being) have the second one. As a matter of building the system, I would still return the first one.
Basically, the second information is important in the sense that you really want an audit trail, but unimportant in that it doesn't tell you why your call failed. Returning it to the caller is useless compared to returning the first error.
But yes, it should certainly be logged and potentially acted on. An application can't do anything useful with the information, but an ops guy can.
So return the first one.
I know it's kind of unhip right now, but I would argue that what you're asking for is better handled using checked exceptions. That really forces you to think about error conditions, and it's enforced by the language. I'm continuously baffled by the argument that this is a bad idea.
> Many programmers in many situations would be perfectly happy to catch "failure to open a file" and "failure to open a database connection" and "failure to connect to a network host" with one simple handler that logs the failure and either aborts, retries or ignores.
aren't exception appropriate a according to a certain portion of developers. The reason they aren't exception appropriate is that you should expect these things to happen from time to time, ie they are not exceptional.
using exceptions to handle them is part of the problem Go attempts to solve by having multiple return values.
Either way it doesn't address the real argument, about where it is appropriate to use try and catch. What is inherently better about multiple return values at every level, compared to semi-centralized catch blocks?
My point was that there is a school of htought that exceptions should only be used in "exceptional" circumstances.
The examples given were not exceptional in that a programmer should expect those types of errors during the normal execution of their program. Therefor exceptions aren't the solution to those types of errors.
Does that make more sense to you?
PaulHoule is explaining why he likes a specific mechanism compared to another, and you are only replying with the previously-established fact that there exists a disagreement here, not a counterargument.
And I think alot of people take my point of view as evidenced by the votes my initial post received.
No worries, thanks for the point of view:)
Burying that kind of every-day, expected-circumstance logic in a side channel is, at least in some developers opinions, detrimental to the readability and understandability of a program.
I am probably one of those people who catches exceptions prematurely and who hasn't learned to use "finally." If you link to some advice on how to use such things, I'll read it. I want to believe that I can learn a better way to use exceptions, but they just haven't clicked for me.
Then again, I've been using exceptions in an environment (C#) that matches the author's JSON.parse() example very well. The .NET library designers already decided what counts as exceptional, and it's often not possible for me, as a .NET user, to decide much of anything about the use or placement of try/catch.
...when you're building real systems, the complexity of the error handling can approach or exceed the complexity of the "normal" path...
I can understand all too well that a complex error handling path is intimidating, tedious, distasteful, and no fun at all. But in every serious project I've been a part of, error and exception handling has been where the bulk of the design, implementation, and testing work was done. That stuff is precisely how serious systems distinguish themselves from toys. Having a catch-all crash-path doesn't change that, because the serious system is not permitted to crash. As the author here points out, that's what assert() has offered for decades anyway.
What do you mean? You can certainly decide what's exceptional. You can roll your own exceptions. You can catch and discard or handle exceptions you don't want to bubble up. You can put try-catch everywhere or nowhere (or choose a reasonable place in between). Your hands are not tied by .net exceptions any more than they are tied by any other reasonable error handling model.
I can decide to make something in my code exceptional, sure. I can't decide to make something in the library not-exceptional, though.
You can put try-catch everywhere or nowhere (or choose a reasonable place in between).
This isn't always the case. Sometimes the only way to answer a question ("Can this string be parsed as an integer?") is to try it and catch the exception. And there's just no way to do that coherently without catching it immediately.
Later releases of .NET do include a non-exception-throwing TryParse() call in many places. I'm pretty sure I've run into some cases where that was not available, however, and I'm pretty sure I've run into similar situations in other methods besides Parse(). And TryParse() was a late addition; look back in the 1.1 or 2.0 docs, and it doesn't exist.
In other words, somebody thought it was reasonable to force .NET users to catch some exceptions immediately.
Your hands are not tied by .net exceptions...
No, but there's not much point in using .NET if I'm not using the library that comes with it. And the design of the library does tie my hands in some cases.
I think the author makes a great point about libraries that use exception handling blurring the line between bugs and expected problems. That's exactly how I feel about the C# work I've done.
Making something in the library non-exceptional is equivalent to discarding an error. Catch the exception and discard it. Done. Do this at whatever level you feel is appropriate (or don't, and handle the exception in a more reasonable fashion).
> In other words, somebody thought it was reasonable to force .NET users to catch some exceptions immediately.
What do you suppose should be done? The other option seems to be to continue in a erroneous state. I'm probably not familiar with every possible error-handling methodology, but it seems the main ones are "error codes" (those worked so great in C, right), "exceptions" (annoying, but error handling in general is annoying), and "injected handling" (where the caller can inject error handling code somehow; but this is more complicated and requires deeper knowledge about the callee). Is there a better option?
> No, but there's not much point in using .NET if I'm not using the library that comes with it. And the design of the library does tie my hands in some cases.
My point is that you are not tied by .Net any more than you are tied by any other exception-handling language. You can handle exceptions where and how you feel is appropriate.
> I think the author makes a great point about libraries that use exception handling blurring the line between bugs and expected problems. That's exactly how I feel about the C# work I've done.
I think this is a bit of a red herring. An exceptional condition is simply a special case. A bug in the code is an exceptional condition. A problem parsing a number is an exceptional condition. Modern languages generally have specific exceptions to allow you to handle different situations with custom logic, but it's important to note that the runtime can't reliably distinguish between "bugs" and "expected problems". Did your number parsing fail because the user entered an invalid value or because your code grabbed column 3 instead of 4?
I guess this is where we differ. I feel that the designers of the .NET library have chosen to throw exceptions in places where nothing exceptional is actually happening, where no error has occurred, where the programmer may very well be expecting the "exceptional" outcome.
Where my code must handle such conditions, forcing me to handle them as exceptions makes my code longer, less readable, harder to change, and harder to reason about.
...it's important to note that the runtime can't reliably distinguish between "bugs" and "expected problems".
This is exactly what the author of the linked piece points out, this is a part of my complaint, and it's an issue Microsoft has tacitly acknowledged the seriousness of by the addition of alternatives to exception-throwing calls, like TryParse().
The problem from my perspective isn't the runtime or the languages that target it, but choices made when the library was designed.
It does not feel to me, as a user of these massive libraries, that there was any systematic way of deciding what should be and what should not be reported as an exception.
When does this actually happen? Are you really expecting it to fail when you open a file, or when you parse an integer, or whatever else? Where is .Net throwing exceptions in cases that no error has occurred and that you expect?
This complaint is common, but it feels rather hollow to me. Most of the time it seems to come down to a preference for error codes over exceptions, or an annoyance with the try-catch boilerplate, rather than a legitimate complaint about exceptions being thrown inappropriately.
> Where my code must handle such conditions, forcing me to handle them as exceptions makes my code longer, less readable, harder to change, and harder to reason about.
Are you suggesting that every function should have two versions like Parse and TryParse? Is this really what you'd prefer the .Net team work on, instead of providing new tools and functionality? Or are you wanting something like "ON ERROR RESUME NEXT" so that you can ignore these "expected" errors?
> This is exactly what the author of the linked piece points out, this is a part of my complaint, and it's an issue Microsoft has tacitly acknowledged the seriousness of by the addition of alternatives to exception-throwing calls, like TryParse().
Eh, the linked piece seemed mostly to be pining for the days of error codes. There's no general way to determine if a failure is a "bug" or "expected", not for exceptions and not for anything else. If you want to avoid exceptions for "expected" failures, then you're asking for no exceptions at all, which is fine, but the problem isn't just the definition of "exceptional".
> It does not feel to me, as a user of these massive libraries, that there was any systematic way of deciding what should be and what should not be reported as an exception.
The systematic way was "it's exceptional if it's not the desired or expected outcome". The addition of TryParse was a nice bonus, but is in itself an exception to the exception model.
Oh, for Pete's sake. I'm not learning what I hoped to here, and you've made up your mind.
Can you give me a practical situation where an exception is thrown despite there not being an error, aside from the canonical Integer.Parse() example? In my experience, that's not the bulk of any practical program, and it's still exceptional from the point of view of the Parse function.
I'd be interested in discussing this, but I'm not really sure what you think would be an improvement. And yes, it's a rather uphill battle if your proposal is to use error codes most of the time. I think that ship already sailed (although there's a strong case for error codes in C++, but that's kind of a special case).
System.ComponentModel.Win32Exception: The operation completed successfully at System.Drawing.BufferedGraphicsContext.CreateCompatibleDIB(IntPtr hdc, IntPtr hpal, Int32 ulWidth, Int32 ulHeight, IntPtr& ppvBits)
However, in the interest of trying to be helpful, my guess is that you have a bug in your app that plays poorly with a bug in Win32. A quick search for BufferedGraphicsContext.CreateCompatibleDIB yielded this question on SO (link to top answer) which indicates a resource leak may be at fault:
http://stackoverflow.com/questions/1209769/system-componentm...
I cant speak for Go but Scala's Option type is very different from C error codes since it is a monad. As a result you can use it with Scala's 'for' comprehensions (which are roughly the same as Haskell's arrows) and write code that looks roughly imperative without having to explicitly handle any errors.
http://www.scala-lang.org/api/current/index.html#scala.Optio...
Go is a systems programming language, and a large part of systems programming is dealing with the potential errors. Take a look at, say, how the Linux kernel implements a system call. In fact, this is do_mmap_pgoff(), which does the bulk of the work for a mmap() system call in Linux: http://lxr.linux.no/linux+v3.1.1/mm/mmap.c#L942
It's almost nothing but error checking, and I submit that is as intended. In this circumstance, you want all of the error checking right in front of you, because that error checking is enforcing very important kernel policy. A lot of kernel code is error checking, because it has a lot of policy to enforce.
You're right that this error checking paradigm is a throwback to C, but Go was designed as a better C. Go's means of handling errors is exactly what I wish I could do in C; it allows every function to both return a value and an error code. It avoids passing in pointer to values because the function returns an error code, or having to check a global errno because the function returns a meaningful value. It's kinda like living in a world where C++, Java and Objective-C were never invented. (I like and use C++, so please don't take that as jumping on the C++-is-the-worst-thing-ever bandwagon.) I find that a very interesting direction, one which should be explored.
I use exceptions for higher-level code. When writing, say, a parser, I'd rather throw and catch exceptions. I don't think we need to choose one error-catching paradigm and declare it's best for all levels of code.
Which is also possible in Go, and template parser from its standard library is, actually, written in this style:
http://golang.org/src/pkg/template/parse/parse.go#L96
Functions call panic() (via t.errorf) to avoid passing errors between multiple levels of functions, but then Parse method catches it with recover(), checks what kind of error it is, and either panics again if it's a runtime error, or returns other kinds of errors.
Of course that's just the tip of an iceberg with no way for IDEs/tools to know what return is an error, no way to know at a glance if "_" was an ignored error or ignored other extra return value, plus other fundamental problems caused by implicit types.
You bring up a good point that when resorting to value comparisons for error handling instead of type comparisons it's easy to make mistakes. The code is as far as I can tell a simplest way to handle an index out of bounds, but to also handle a 'slice out of bounds' it would need to also compare to the string value "runtime error: slice bounds out of range" -- not helping the case for error handling in Google Go.
This code strikingly shows deficiencies in Google Go non-local error handling:
- Tons of boilerplate (defer, recover, re-panic, type check, value check)
- Not scoped so can only handle an error once per function
- Have to do value tests in addition since types are very generic due to implicit interfaces
- Error values are poorly defined
- Result for higher-level caller is buried deep in the function and non-obvious
These problems extend to all non-local error handling in Google Go, not just for this specific case of array and string indexes. That the idiomatic way to 'handle' an error is to abort the program is another separate problem.
...
> But still, nothing is as bad as the common "On Error Resume Next" that so many terrible VB programs start with.
Assuming "On Error Resume Next" does what I think it does, you can't complain about both of these at once.
A non-typed exception system (that doesn't solve the problem with another mechanism) just sounds like a very bad idea. Is Javascript like that?
http://www.az2000.de/pics/screenshots/Screen%20Shot%202011-1...
It used to be that Microsoft's website took up lots of fixed upper screen real estate for "branding." Now they have fixed it and is much more usable. Have the Microsoft designers gone to work for Google now?
I would like the author to expand a bit instead. In python there are typed catches, it seems to make a lot of sense to me: you catch only the exceptions you want, and let the other ones bubble up. It is well explained in Martelli's Python in a Nutshell.
I have seen try/catch construct in Java and Javascript, and it is probably less readable, and certainly can be over-used, but in Python, returning None in all exceptional cases is annoying, different issues got merged into a single "Muted" case.
http://blog.orbeon.com/2011/04/scalas-optionsomenone.html
This is like saying that walking is faster then driving because I can walk 5 meters faster then it takes me to get into a car. Yes, error codes are more compact in a tiny "Hello world" example because it is only showing one function call. Exception handling becomes more compact when you're writing something less trivial and you don't have to repeat the same error handling code after every call.
> Try/catch is goto wrapped in pretty braces. There's no way to continue where you left off, once the error is handled.
Don't throw exceptions if you can handle the error and continue where you left off. Exceptions is for when you can't continue. Think of throwing as a way to roll back transaction, stop whatever you were trying to do, and go back to the last consistent state.
Obviously, exceptions are not perfect. As author correctly notes, they require careful consideration of what's exception and what's part of normal flow. But they were invented for a reason, and I don't see the article offering any alternative solutions to the problems that exceptions are solving now.
Yes, although quoting that example doesn't support the assertion. JSON.parse is a library function. How can it judge whether the caller can continue or not just because the JSON cannot be parsed?
> Think of throwing as a way to roll back transaction, stop whatever you were trying to do, and go back to the last consistent state.
Any try block that is larger than one atomic operation can become a nightmare to roll back, since the catch gives you no idea how far it was into the block, what resources were allocated, etc. So although try/catch avoids the hassle of checking state after each operation, you pay for it on errors.
Don't make assumptions about the caller, throw if your library can't continue.
> So although try/catch avoids the hassle of checking state after each operation, you pay for it on errors.
If your language supports RAII (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initial...) you get a free ride. Otherwise you have to write non-trivial cleanup code regardless of how you return errors. Try/catch (and especially finally) helps a lot because you get to group all your cleanup in one place. With error codes the code looks something like:
This is a maintenance nightmare. It's hard to see what the code is doing because the real logic is hidden between piles of error handling stuff. If you add a new resource acquisition in the middle you have to go over every statement that follows and modify error handlers. And don't you dare changing the order of statements, because that basically forces you to rewrite the whole function. Believe me, you don't want this in your code, exceptions are your friend :)Well, if his complaint is about untyped catches, well, I guess they must suck. But if he thinks that typed catches are somehow worse (hint: they're better), he is wrong.
> But still, nothing is as bad as the common "On Error Resume Next" that so many terrible VB programs start with.
Ok, everyone who always checks the return value of printf() in C, raise your hands. That's what I thought. The semantics of C is that whenever something fails, you just continue from the next line (statement, whatever...) even if the world is burning. Try-catch may suck, but checking for return values sucks more (in languages without pattern-matching), deal with it.
While I tend not to be a big fan of try/catch myself, one thing I like about Objective C and Cocoa is that they at least provide classes to separate programmer mistakes (NSException, used with try/catch) from application issues (NSError, used however it best fits the app). If the try/catch pattern must be used, I think its sensible to have this kind of separation of responsibilities.
Before I worked with Qt, I never thought this was possible without sacrificing the API, but the Qt API is very clean and doesn't seem to suffer from that design decision. So I looked around in the Qt API for some time, trying to learn how they managed to get along without exceptions. And I think I finally got it.
The whole error handling in Qt mostly boils down to providing sensible null objects. That is, instead of using NULL pointers (as in C) or generic null objects (as in JavaScript), for all kinds of objects there are specialized null objects which behave sensible to the most possible extent.
However, if 'meh' could be there, it's better to use an error style such as:
And continue without raising because there's no need to raise as there's nothing exceptional here.Ideally, raising an exception should have the meaning "Hey, something is wrong here and I don't know what to do next". And someone in the call hierarchy would handle this and say "Oh, the connection stopped.. that's why it's not working. I'll reconnect and call you again".
And note that this "parent handling" might be way higher than where the problem occurred..
There's no inherent reason an exception-throwing method can't be invoked in a style that gives back a value/exception pair. That limitation is a property of the specific language, not all languages. For example: exceptions could be caught succinctly if method invocations prefixed with 'catch' returned a value|exception tuple (`v, ex = catch ParseInt("test")`).
The primary difference between exceptions and error codes is the default behavior. Error codes default to 'ignore' and exceptions default to 'propagate'. The rest of the differences (requiring acknowledgement, succinctness of handling, availability of stack traces) are more of a coincidence based on the common decisions in languages like Java, Go, C, C++, python, etc.
I think the main downside of existing exception implementations is they discourage programmers from defining useful error cases for functions. It's so easy to propagate or suppress that anything else feels like massive busy work. Simple improvements would be to make wrapping exceptions easy and defining new types easy (throws FileNotFound as forge MissingNetConfigFile).
On the other hand, the main downside of error code implementations is the ease of ignoring. Go has a very good idea in the "x, _ = funcWithIgnoredError" style, but it extends poorly to otherwise-void methods like 'flush stream'.
Even better, when stack traces in exception logs are examined, it's easy to see where in the call chain things are going wrong.
During a database transaction for example, something might go wrong 5 method calls deep, and I should roll back the transaction.
If I'm forced to either use checked exceptions or use if statements to check whether there was success at each level, then that's a lot of repeated boilerplate try/catch/rethrow or if(success) code which unchecked exceptions free me up from having to write.
If all I receive at the transaction level is a failure code, then what do I log, other than "something went wrong?!". With exceptions, all the work is done for me. I just log the exception stack trace and I can easily see what needs to be fixed.
Much of the time, exceptions are useful because they are thrown becuase of something you didn't anticipate happening, as opposed to something you had planned for when writing the code. And when that unanticipated thing does happen, there is nothing more beautiful than an automatically generated stack trace telling you exactly what happened.