4 ms·
I'd be interested in hearing more about the exceptions side. The usual derision is that some top level unrelated code ends up handling them, but that seems sil
by rogerbinns 14y ago
I'd be interested in hearing more about the exceptions side. The usual derision is that some top level unrelated code ends up handling them, but that seems silly. A far more normal example would be a routine that has to get some resources and calls various retrieval routines which may end up accessing the filesystem, databases, or the network. Those routines could go several calls deep before throwing an exception. The top routine can then find alternatives, use cached versions, return an error etc.
I do like exceptions in Python where their use is ubiquitous and there is garbage collection. I detest checked exceptions in Java because you are forced to handle things at a level you often don't want to.
I'm 65% certain Go made a mistake not using exceptions, but would love to hear from others.
- zmj 14y agoGo uses the Error type and multiple returns in the scenario you describe. Code might be something like this: foo, err := GetDataFromDatabase() if err != nil { foo, err = GetDataFromLocalFilesystem() } if err != nil { foo, err = GetDataFromNetworkFilesystem() } return foo, err There is an exception-like mechanism called 'panic', but it shouldn't be used as a control flow tool. Panic is reserved for unrecoverable errors, not mundane situations like a failed query.
- stickfigure 14y agoThis seems extraordinarily painful. I usually have five or six stack frames between GetDataFromDatabase() and code that can raise error dialog to user or return HTTP error code. This means every single one of those stack frames are going to duplicate this annoying if err != null sequence. The article author is quite wrong when he says "exceptions are broken by design because they force errors to be handled at points far away from the cause". Exceptions don't force you to handle errors at any particular location; they allow you to handle errors anywhere in the stack. On the other hand, return values do force you to handle errors then-and-there, which 90% of the time is a half-dozen stack frames away from your handler.
- iand 14y agoNot at all. You can pass error values back up the call chain if you want to.
- stickfigure 14y agoRight, by typing a zillion if err != nil return err lines of code. In any sophisticated app, almost every single function call is going to require this boilerplate. Annoying.
- taliesinb 14y agoWe have two orthogonal tasks which traditional exception mechanisms complect together: 1. Handling a failed assumption, which will cause the code that follows to be incorrect (I need the contents of this file to do my task, but it doesn't exist, so I can't do my task) 2. Handling a failed operation, which there is a straightforward way of working around (if I can't write to my log file, maybe write to stderr instead instead and give up) The trouble is that the 'inner' code doesn't know which of these a given failure is, because it is context specific. Seems like with languages like Java, the default is 1, whereas with Go it is 2. They're kind of equivalent, though, because you can always convert a !ok into a panic(...). But certainly exceptions are the nuclear option, so it seems reasonable to me that they shouldn't be the default for common operations that can fail.
- stickfigure 14y agoWhy wouldn't you want to use exceptions for #2? I posit that it is very rare for errors like RPC failures and filesystem errors to be handled at the level of the call. 90% of the time the natural handler is several stack frames up where you can raise an error dialog or write an http error response. Return values create endless repetitive "if error return error" code, or worse - programmers get lazy and ignore return values, producing bugs that show up only in hard-to-test-for situations like transient network failures.
- agentS 14y ago
- taliesinb 14y agoIs there a particular reason why? I mean, if the failed query requires that a potentially quite involved operation needs to be aborted, an operation that is several functions deep, isn't panic exactly appropriate?
- koenigdavidmj 14y agoGo has a two-clause if statement: if foo, err := GetDataFromDatabase(); err != nil { if foo, err = GetDataFromLocalFileSystem(); err != nil { foo, err = GetDataFromNetworkFilesystem() } } return foo, err
- rogerbinns 14y agoEvery Go article explains this, but it doesn't answer the higher level question of why not use exceptions. Even worse you are forced to return data values along with the error code/flag/object. I've not seen a standard error object either which means every place that looks at an error has to be intimately aware of what it is looking at. There is an os.Error but that appears to be string only so there is no errno equivalent value inside. In languages with exceptions there is a base error/exception/throwable that has reason fields as well as methods to get tracebacks which are extremely useful. I'm now 85% convinced Go made a mistake not using exceptions.
- rogpeppe1 14y agoIf you're going to do error handling right in an exception-using language, you really need to catch every exception close to its source, otherwise you won't know how to handle it properly. I think Go's main approach is this: errors are not exceptional. Errors happen all the time, and should be considered part of the normal control flow of the program, not something to be shoved into a hidden compartment and dealt with later. Thus using the usual control flow statements to check for errors is entirely appropriate, and it's immediately obvious when reviewing the code if someone has failed to do the right error checking. BTW there is no os.Error type any more. There's a language-defined "error" type, which is an interface containing a single method: type error { Error() string } You only need to be intimately aware of what you're looking at if you want to take some different action based on the kind of error. This is actually quite rare (usually you care only that something failed, not how it failed), but when you need to, generally the calling package defines an error type that represents a particular kind or class of errors (for example http://golang.org/pkg/os/#LinkError http://golang.org/pkg/os/#LinkError)
- rogerbinns 14y agoWhen you have exceptions you can decide how many levels of stack frames higher to handle it, and none of the intermediary functions have to be modified. With this mechanism, every intermediary must handle the error and must agree on the error type (to some degree). The argument that keeps being trotted out for Go's approach is consistency of handling, which is a good thing. But it is very manual especially where there is distance between the code that finds an error and the code that decides what to do about it. Doing all that manual work doesn't seem to have any benefits to me.