Live data from Hacker News

Isaacs: try/catch is an anti-pattern

groups.google.com

111–120 of 140 posts

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

#111
post #106

Earlier quoted context omitted.

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

> 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... ) yo…

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.

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

#112
post #70

Earlier quoted context omitted.

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

You can certainly decide what's exceptional. 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.…

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

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

#113
post #50

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

It was pointed out later in the thread that it should have been 'undefined' rather than 'null'.

I'm not keen on exposing undefined to external code; it inflates it into a “valid” value like null has become.

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

#114
post #109

Earlier quoted context omitted.

I'm genuinely curious: how is returning undefined any different from returning null, in this case?

If you return null, there is no way to discern between invalid input data and 'null' as input. If you return null for 'null' as input and undefined for invalid data, then you don't have to look at the input again to check whether you just failed to parse json or whether the input was just 'null'. if (HypotheticalJSON.parse(input) === null && input.trim() != 'null'){ alert('invalid input'); } instead of just if (Hypot…

Ok, so its the case that null is valid data and makes for a bad "invalid value".

Personally, this demonstrates a core reason why return values (and returning null in particular, in any language) are a bad way of handling errors - they're a leaky abstraction and too much inside knowledge is required to use them safely. For example, if I have code like

    a = foo()
    if (a is valid) bar(a)
I have to know about the possible values of a before I can pass it to bar. Exceptions are better because I don't have to know the internal details (foo and bar could be library functions that return some data I don't know all the valid values of):

    try {
        a = foo()
        bar(a)
    } catch (e) {
        handle error here
    }
But I also agree with the post that exceptions aren't always great either.

Instead of returning null, I like haskells Maybe. You either return a valid value (which can be null, in this case) or it returning Nothing, which can never be something which is also valid data. This removes the possible ambiguity of whether null is an error value or a valid value and the haskell compiler makes sure you hanbdle the error case, but its still a signal-error-through-return-value method of error handling and is not always ideal. I also like the ideas behind common lisps condition system, though I have never used it in real code. I like the idea of abstracting the error signalling, handling and routing into separate entities that can happen at various parts of the hierarchy. I also like Go's defer/panic/recover mechanism, though, again, I have never used it in real code, so don't know how well it works in reality.

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

#115
post #33

Earlier quoted context omitted.

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

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

Return the first one. You always care more about the original error than an explanation of why you failed in cleaning up after it.

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

#116
post #33

Earlier quoted context omitted.

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

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.

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

#117
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…

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.

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

#118
post #112

Earlier quoted context omitted.

You can certainly decide what's exceptional. 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.…

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

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.

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

#120
post #47
post #33

Earlier quoted context omitted.

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

> Look at how much rock stable C software we have out there. Which would be what exactly. The work of Knuth and djb I'll grant you, but the rest?

Didn't Knuth write TeX in a variant of Pascal?
Post reply on HN