3 ms·
For me, no exceptions is a show-stopper. I know people sometimes say it's actually advantage, and I believe in some situation, it can be. In a typical scenario
by norswap 13y ago
For me, no exceptions is a show-stopper. I know people sometimes say it's actually advantage, and I believe in some situation, it can be.
In a typical scenario, I might be working on an utility that does some IO. In case of error, I just want to do some cleanup then exit. With exceptions, the control flow will jump right up to the places were cleanup has to be performed. With return values, I need to check return values at each step of the call stack, which is tedious and ugly. Return values have to be modified to account for error code. Admittedly, Go handles this last point well with multiple return values.
- deleted 13y ago[deleted]
- mediocregopher 13y agoGo's version of 'finally' is called 'defer', and it can be used in any situation, not just error management (I often use it for releasing locks in the statement immediately after I've locked them). >I need to check return values at each step of the call stack, which is tedious and ugly. I see it more as explicit. You HAVE to decide what you're going to do in case of an error. Usually it's as simple returning that error, but sometimes you want to do more, and having the question constantly being asked means you think about it for all cases.
- pcwalton 13y ago> You HAVE to decide what you're going to do in case of an error. This is not true in all cases: func f() error { ... } func main() { f() // OK fmt.Println("Uh-oh") }
- deleted 13y ago[deleted]
- epidemian 13y ago> I see it more as explicit. You HAVE to decide what you're going to do in case of an error. I've often read this argument, but i don't see how that is an advantage over using exceptions. With exceptions you don't have to decide what to do in every function call that might raise an error. You just have to know that they can raise one, and code accordingly. For example: f = File.open(...) foo(f) bar(f) f.close() Is buggy if foo or bar could raise exceptions (the file would not be closed if an exception is raised there). Fixing it could imply adding a `finally` block around f.close() so the file always gets closed (same as Go's defer) or using a higher order function that already does that for us: File.open(...) do |f| foo(f) bar(f) end The thing is, this code didn't need to worry about which of foo or bar was raising an error; it just needed to worry about closing the file if anything went wrong. And i could add a new baz(f) call in there, or maybe refactor the foo(f) and bar(f) calls into a foobar(f) function that does just that and the code will still be as linear. There'd be no need to add extra `if`s there nor in the refactored foobar(f) function. > Usually it's as simple returning that error... I'd say that it's rather "most of the time" than "usually". And exceptions give you exactly that default behaviour of propagating the error up the stack. Without having to obfuscate the logic of some intermediate code (that doesn't care about what error could happen, as long as they are propagated to the caller) with superfluous `if`s and `return`s.
- pdonis 13y agoWe had a HN thread some time ago about this issue: https://news.ycombinator.com/item?id=4562211 https://news.ycombinator.com/item?id=4562211 I agree with your take on it; I blogged about it at the time that previous HN thread came up: http://blog.peterdonis.com/rants/python-v-go-error-handling.html http://blog.peterdonis.com/rants/python-v-go-error-handling....
- dragonwriter 13y ago> I've often read this argument, but i don't see how that is an advantage over using exceptions. Go's way is better than Java style exceptions because Go has exceptions very much like Java's ("panics"), but Go isn't as likely to force you to handle them to address circumstances that aren't exceptional in the context of your use case, because the builtins and standard library don't tend to use them as much, preferring reporting conditions through multiple return values, which allows the decision to panic or not to be made by code written at a level that has some awareness of the use being made of the function so that it knows whether a condition is one which warrants a panic.
- norswap 13y agoDidn't know about defer, cool. You can use Java's finally in a similar manner (although the syntax is worse, and you can't scatter the logic like with defer). > I see it more as explicit. You HAVE to decide what you're going to do in case of an error. Usually it's as simple returning that error, but sometimes you want to do more, and having the question constantly being asked means you think about it for all cases. Checked exceptions also do that (you have to add "throws" statements) and it looks much cleaner. I must however say that I'm not a fan of checked exceptions either.
- dom96 13y ago> Checked exceptions also do that (you have to add "throws" statements) and it looks much cleaner. I must however say that I'm not a fan of checked exceptions either. Indeed. I was going to mention checked exceptions too. I think they are a great alternative, and personally I do not wish to use a programming language with no exceptions. I must ask, why are you not a fan of checked exceptions?
- norswap 13y agoFor the same reason I don't like errors in return values: most of the times you cannot recover from failure, only do some cleanup. Since you should always consider than anything you call may fail and put your cleanup in a finally/defer, you don't really need checked exceptions. Even in say, a GUI application (which you don't want to crash on failure), you'll usually contain all failures at some level and for instance display a pop-up indicating that something failed. I suppose checked exceptions are good for recoverable failures, but I encounter preciously few of those.
- deleted 13y ago[deleted]
- dragonwriter 13y ago> For me, no exceptions is a show-stopper. > For me, no exceptions is a show-stopper. Go has exceptions, but it differs from, e.g., Java in that: 1. It handles raising and recovering from them, and specifying actions that must happen whether or not an exception occurs, through two built-in functions (panic/recover) and a simple statement (defer) rather than a multi-armed structured syntax (try/catch/finally). 2. The built-in functions and standard library use panics (Go's exceptions) with more restraint than is idiomatic in Java. > In a typical scenario, I might be working on an utility that does some IO. In case of error, I just want to do some cleanup then exit. With exceptions, the control flow will jump right up to the places were cleanup has to be performed. And this works fine with Go's panics. You'll be more likely to need to decide that something is panic-worthy and explicitly panic in your lowest level call in Go than in Java (just as, conversely, you'll be more likely to need to explicitly catch and handle an exception thrown in Java for something that's not really exceptional in your use case.) But aside from that, there's not a lot of difference. > With return values, I need to check return values at each step of the call stack, which is tedious and ugly. Or check them at the lowest level of user code and, if there is a problem that is truly exception/panic worthy at that level, call panic(). If you are checking errors at each level of the call stack just because the lowest level function uses (as is typical in Go) a secondary return values to report conditions outside the scope of the main return value, you aren't dividing functionality up properly between functions. Generally, I would think, the lowest level of user code that is specific enough to make an intelligent decision should either be collapsing out the unusual-condition secondary return value or initiating a panic, depending on how the condition involved relates to the purpose of the code.