10 ms·
Isaacs: try/catch is an anti-pattern
- AndrewDucker 15y agoI 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.
- ianso 15y agoI 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...
- losethos 15y agoI 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.
- Daishiman 15y agoThe 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.
- wladimir 15y agoBut 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.
- josscrowcroft 15y agoThis 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.
- ecaradec 15y ago> "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.
- slowpoke 15y ago>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.
- zbowling 15y agoThis 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.
- dolbz 15y ago> 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
- Sandman 15y agoThat 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.
- jwr 15y agoAnybody 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: http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html 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.
- jrtipton 15y agoI 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')
- dchest 15y agoThis seems to be exactly the attitude of Go: "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 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: f, _ := os.Open("filename") "_" 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. f, err := os.Open("filename") If you don't use "err" later, this won't compile. f, err := os.Open("filename") if err != nil { // handle error } This code handles error.
- PaulHoule 15y agoThis 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.
- klodolph 15y agoI'm not sure how I'm supposed to read this... I guess I need an account?
- dchest 15y agoSometimes Google Groups ask for an account, sometimes don't -- not sure why. Anyway, here's the post: http://pastie.org/2861111 http://pastie.org/2861111
- ericflo 15y agoBruno Jouhier's response nails it. It's funny, but for whatever reason I almost always disagree with Isaac's (perfectly reasonable) points of view.
- qntm 15y ago> Try/catch is goto wrapped in pretty braces. There's no way to continue where you left off, once the error is handled. ... > 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.
- michael_dorfman 15y ago"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."
- mattdeboard 15y agoOne of my personal pet peeves is seeing: "try: foo() \ except: pass" in code.
- jorangreef 15y ago"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."
- perfunctory 15y agoThe core of the post: 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). Totally agree.
- morsch 15y agoIn 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?
- thomasz 15y agoIt 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.
- albertzeyer 15y agoOT: Was it always like this that the top 170px are just fixed on groups.google.com and thus are somehow wasted from the horizontal space? http://www.az2000.de/pics/screenshots/Screen%20Shot%202011-11-14%20at%2012.29.56.png http://www.az2000.de/pics/screenshots/Screen%20Shot%202011-1...
- mike_esspe 15y agoIt's a new GWT based design
- jjclarkson 15y agoAt 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.
- ww520 15y agoOMG. 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?
- shin_lao 15y agoAs we say in C++ "exceptions should be exceptional".
- gbog 15y ago> 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.
- efsavage 15y agoTry/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.
- apl 15y agoExceptions aren't the one and only solution to 'null' issues in Java et al. http://blog.orbeon.com/2011/04/scalas-optionsomenone.html http://blog.orbeon.com/2011/04/scalas-optionsomenone.html
- azov 15y ago> You can't reasonably argue that this: try { foo = JSON.parse(input) } catch (er) { return "invalid data" } is more lightweight than: 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.
- dmethvin 15y ago> 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.
- azov 15y ago> 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_Initialization 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 :)
- jronkone 15y ago> 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.
- epenn 15y agotry/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.
- vog 15y agoThis 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.
- phzbOx 15y agoI 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..
- gaius 15y agoYou mean collections.defaultdict http://docs.python.org/library/collections.html#collections.defaultdict http://docs.python.org/library/collections.html#collections....
- TylerE 15y agoNo 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.
- gaius 15y agoI'm confused - where does that come from? >>> x.getDefault(1, 'Que?') Traceback (most recent call last): File "<pyshell#2>", line 1, in <module> x.getDefault(1, 'Que?') AttributeError: 'dict' object has no attribute 'getDefault'
- TylerE 15y agoBecause it's actually (somewhat confusingly, called setdefault). >>> a = {1:'foo',2:'blah'} >>> a.setdefault(1,'default') 'foo' >>> a.setdefault(4,'default') 'default' >>> a {1: 'foo', 2: 'blah', 4: 'default'} This actually updates the underlying datastructure. Very handy when you want to deal with nested structures, since you can do something like: matches.setdefault(keyword,[]).append(s) This will append s to the list in matches[keyword] if it exists, or set matches[keyword] to [] and then append.
- Strilanc 15y agoNote 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'.
- adamjernst 15y agoIsaacs 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.
- reidmain 15y agoThis 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.
- srgseg 15y agoThe 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.
- pilif 15y ago> I much prefer php's json_decode function, since it just returns `null` on invalid input. A function which has the same result in case of an error as when given valid input (hint: 'null' is a valid json string) is neither good design, nor something I would actually 'prefer'. Aside of that (and more to the point of the original article), I do believe that exceptions can be very useful the deeper the abstraction of your libraries gets. If you are 'far down' the stack and you want to ensure that you are in a position where it's safe to proceed and making it very, very clear for the caller that something went wrong, trow() is the perfect tool in order not to be called with a garbage argument on a successive call to a different function (hint: error results tend to get ignored). And if you are a user of a library, getting nice exceptions can be very handy too - sometimes even wrapping and re- throwing them. A very good example of real code: In a command line script which processes a lot of text-data to import into a database delegates to various importer classes depending on import line type. All these importer classes do their thing and whenever they come across an issue, they just throw an ImporterException with all the context that they know about. The parent script only has to deal with one single case of Exception to produce nice error messages and show everything about the context where they happened. I can make the importers as complicated as they need to be and I never have to check a single return value (or forget to check it). There is one central place to handle whatever kind of Error that can creep up. This is very handy. Granted, in JS/node where each callback cleans out the stack so that a thrown exception can't be handled by whatever caused the callback to be executed later), exceptions are useless and actually harmful because they can mess up the program flow (which assumes callback to be triggered eventually). But exceptions being useless in one environment doesn't make the useless everywhere.
- Dylan16807 15y agoIt was pointed out later in the thread that it should have been 'undefined' rather than 'null'.
- dkersten 15y agoI'm genuinely curious: how is returning undefined any different from returning null, in this case?
- colanderman 15y agoI think a lot of programmers use try/catch when what they really want is Prolog-style failure: my_parser(String, Output) :- parseJSON(String, JSON), extract_the_values_I_want(JSON, Output). if my_parser(String, Output) then ...do stuff with Output... else ...show user "couldn't parse" message... I've been using Mercury (a strongly typed Prolog) for years now and have never once found the need for exceptions (which Mercury supports) to indicate anything other than catastrophic failure.
- sgt 15y agoWhat are your motivations for using Mercury? Are you using it for hobby projects, research/academic purposes or actual production code?
- colanderman 15y agoHobby projects and hobby research. It has some pretty intense features if you're into type theory. IMO the library support isn't yet there for production code (e.g. no or only rudimental GUI/networking code). But if you're just using it for computation (e.g. AI or compiling or some such) it's pretty solid, and its FFI is the most straightforward I've ever seen. FWIW I hear that these people: http://www.missioncriticalit.com/ http://www.missioncriticalit.com/ and these people: http://www.princexml.com/ http://www.princexml.com/ use it for production code, but I don't know much about them, beyond that Håkom Wium Lie (of CSS and Opera fame) is the Director of the latter. I maintain a blog about Mercury's features for interested users: http://adventuresinmercury.blogspot.com/ http://adventuresinmercury.blogspot.com/ if you're interested.
- sgt 15y agoI'm definitely interested. Thanks. I've now bookmarked your blog.
- viraptor 15y agoIt looks great on simple examples, but now consider your stacktrace is 5+ levels deep with some generators doing their generalised work on some collection. If the last function fails, then you better be sure that every single level: - returns the error message and context instead of just error code - wraps the internal problem properly inside its own error description and context - cleans up all internal state Guess what... that's exactly what exceptions do. If don't do the first point, you'll end up with multiple causes producing the same error number. If you don't do the second, you'll get "invalid data" when parsing json (where? what data? which line? which element? why is it invalid?). If you don't do the third, you're going to crash anyway. So if you don't use exceptions to make your code nicer, you're going to end up implementing the same flow over and over again in places that could just allow the exception to pass through. You might even write some helper/macros functions to... add a file and line number when wrapping previous exception. It feels like reinventing the wheel. Edit: Forgot to mentioned the collection processing in the end. What is tricky about it is that for a common "map()" to stop on error, you'll need to have a common way of handling errors. Exceptions do this just fine, custom methods - not necessarily.
- romaniv 15y agoExceptions can make your code much shorter and readable by the virtue of centralized error handling. However, try-catching can be pretty ugly in cases when you need to handle exceptions right away. D language has scope guards to help this. I think we need some kind of better syntax to handle cases like this, but getting rid of exceptions entirely is a bad idea.
- exDM69 15y agoAn interesting read. It's always nice to see people stating their unpopular opinions with proper reasoning. The author's point of view seems to be pretty Javascript/Node.js -centric. I can relate. try/catch and node.js -style asynchronous coding do not work together. However, I'd say that it's the async programming model that is broken, not try/catch. Writing code in node.js async style is very difficult for humans to do (at least for me), but compilers are excellent in this type of program transformations (continuation passing style). The Haskell and Erlang compilers and runtimes do this automatically. You write regular imperative code but it's executed in an asynchronous manner using co-operative fibers (maybe together with native os threads). A similar solution is possible with an interpreted language that has some kind of continuations. Every blocking system call could be translated to a co-operative fiber context switch using kqueue/epoll. What is really baffling to me is that none of the popular interpreted languages (javascript, python, ruby) seem to have decent continuations. I don't know of a reason for not implementing continuations in an interpreter, is there any? Using continuations, one could implement pretty nice async operations and exception handling. Using return values for error handling is really painful. I've been writing a system call heavy C program, and about 40% to 50% (in LOC) of the code is error handling. In test code coverage, I can reach just over 50% branch coverage as most error conditions don't happen while testing. I don't know of a nice way to make e.g. the network fail at the right time to test my error handling in that case. C++ is pretty nice when it comes to error handling and exceptions as resources are cleaned up on error. Python's with..as statement or Haskell's Control.Exception.bracket can do the same thing (the latter is just a regular function, not a language builtin). In C, most error handling is required to free some memory you allocated earlier, but even in a garbage collected language you still have system resources you need to free (file handles, sockets, db connections etc).
- nogden 15y ago>> However, I'd say that it's the async programming model that is broken, not try/catch. This is an interesting point of view, however I'm inclined to disagree on the basis that the async model is representative of how things actually happen; the imperative model is not. In an asynchronous architecture, the developer is concerned with only the current state and the set of all events that may cause a transition from that state. Whether or not an event is the result of an error condition is irrelevant, one simply invokes a different handler. I used to consider the asynchronous model much harder to program in, that is until I changed my thought process to work in terms of state machines. Then it actually became much simpler to program in this model since, at the handler level, one has no preconception about what should happen next, only what can happen next and how to handle each of those situations. >> C++ is pretty nice when it comes to error handling and exceptions as resources are cleaned up on error. There has been many a discussion about the benefits vs. pitfalls of C++ exceptions. I personally am not a fan since the lack of garbage collection means that there are situations that require lots of tedious and error prone boilerplate code just to ensure that all resources are cleaned up. I have to admit that perhaps the nicest way I have seen of handling failure is in the concept of Monads in Haskell (and other functional languages). The ability to add the context of failure as a possibility in any computation, without requiring that computation to explicitly consider it is extremely elegant. Add to that the safety of always returning a well defined value that is fully type checked by the compiler and I believe you have a recipe for very effective error handling.
- tef____ 15y ago''Try/catch is goto wrapped in pretty braces.'' From this I can infer that lambda is also an anti pattern. But as good as this straw man post is, yes you can mis-use exceptions, No that doesn't make it an anti pattern.
- Jach 15y agoGlad I wasn't the only one thinking of Lambda: The Ultimate GOTO
- FuzzyDunlop 15y agoThe way I see it exceptions are helpful in creating self documenting code if used thoughtfully and effectively.* Some might argue subclassing the base exception to create more specifically named ones is a bit silly, because you may be doing little more than renaming a class several, maybe many, times. But it may be countered that this is simpler than determining a list of error codes and then leaving it to other people to find out what a random string or integer even means. I personally haven't found this helpful for debugging. Both ways are better than something just returning false and leaving you to figure out why it did that in the first place. The main benefit to me, however, is to be able to throw the exceptions deeper in the code (where appropriate) and to be able to then catch them at the very last moment. While not perfect (I can't account for everything), it allows me to keep my error handling code cleanly separated from the rest of it at the most abstracted point. Anything lower level will by necessity be a little messier. eg. not wrapping an entire* script in one try/catch block.
- jakejake 15y agoI think a good rule is that if you're not planning (or able) to confidently deal with an exception and put the app into a known state, then don't catch it. Some developers seem to have a fear of allowing any errors to be seen by the user and so they have a habit of swallowing exceptions. I guess it makes them feel that the code is more stable, but it actually masks bugs and makes them impossible to troubleshoot. I have a name for these - I call them "insidious bugs" especially when they result in data loss, which is common with those types of bugs. I was just in some code the other day that was littered with a bunch of these: try { ... code here ... } catch(e) {} not even a console statement or anything! It took two of us over 6 hours chasing down what should have been a 15 minute bug due to that code.
- kstenerud 15y agoRule #1 of programming articles: Be wary of anyone who states "always" or "never" or "harmful" or "evil" with regards to a code construct/idiom/pattern. What they're telling you, in effect, is that they know everything there is to know about X, and so you should trust their judgment and not think for yourself. And in every case I've seen so far (including the famous goto debacle), they have merely focused on their pet code, ignoring the actual cases where the construct is useful (most often, they'll include trivial examples when the construct is meant to be used in complex situations), and this article is no exception. When your argument becomes "we should throw this out because bad programmers use it badly", you have no argument.
- dasil003 15y agoExceptions vs error codes seems closely related to dynamic vs strong-typing ala Haskell. If you use error codes exclusively, then it's possible to do much stronger reasoning about all code paths, and you can create a more robust system. It doesn't have the magic of the static analysis that Haskell's type system enables, but there is a certain "purity" in that no function can have an error pass through it in the call stack without being aware of it. This is exactly what you need in systems programming like an OS kernel where every conceivable thing will go wrong and your goal is to handle everything gracefully. On the other hand, when writing business apps, you not only have the technical concerns of the system, but your program is primarily a big hairy rat's nest of business logic which may never achieve well-definition. In such circumstances, you never get to a point of stability where you can be so pedantic about the technical details because you have your hands full just trying to meet the ever-changing business requirements. Exceptions are a great help here because they allow you to handle new errors with minimal ceremony and function redefinitions. Just like Ruby duck-typing, this means you can refactor faster, but obviously in a dramatically less robust fashion than Haskell. But if you're operating in an environment where a logical failure here or there is acceptable and code has a short half-life, then you'll get better ROI from duck-typed, unit-tested, exception-notifying code than you will from nearly-impossible-to-fail statically-typed code. A lot of code probably falls into a gray area where it becomes largely a matter of programmer taste.
- je42 15y agoI dont understand this. According to some believe exceptions should be used in an exceptional context. Can anybody define what exceptional is? I use exception to get the context in which the error happend. Call stack, variables, stuff like that. I can log it, i can give good error message to the user. I never think about if an error is exceptional.
- th0ma5 15y agoAnyone have an opinion with regards to Ada is this context? From what I've read from Ada it tries to do soft errors as a rule.
- ghc 15y agoHow on earth can types catches be worse than untyped catches? As a Pythonista, this whole argument makes my head hurt. He's railing against everything considered beautiful about exception handling in the Python community.
- enduser 15y agoIt's only an anti-pattern in languages that don't have resumable conditions (i.e. other than Common Lisp).