Live data from Hacker News

Isaacs: try/catch is an anti-pattern

groups.google.com

31–40 of 140 posts

Re: Isaacs: try/catch is an anti-pattern

#31
post #9

This 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 overloa…

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.

Re: Isaacs: try/catch is an anti-pattern

#32
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.

Re: Isaacs: try/catch is an anti-pattern

#33
post #9

This 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 overloa…

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 co…

> 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.

Re: Isaacs: try/catch is an anti-pattern

#34

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.

Exceptions aren't the one and only solution to 'null' issues in Java et al.

http://blog.orbeon.com/2011/04/scalas-optionsomenone.html

Re: Isaacs: try/catch is an anti-pattern

#35
post #24

> 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 functi…

> 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.

Re: Isaacs: try/catch is an anti-pattern

#36
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.

Re: Isaacs: try/catch is an anti-pattern

#37
post #8

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: 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…

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')

Re: Isaacs: try/catch is an anti-pattern

#38
post #9

This 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 overloa…

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 co…

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.

Re: Isaacs: try/catch is an anti-pattern

#39
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..

Re: Isaacs: try/catch is an anti-pattern

#40
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'.

Post reply on HN