4 ms·
I think that misses the point -- explicit behavior is always more desirable than implicit behavior. I don't think most functions end up having an error result b
by xyzzy_plugh 9y ago
I think that misses the point -- explicit behavior is always more desirable than implicit behavior. I don't think most functions end up having an error result by convention -- many functions never return errors. Treating errors as values means the flow of control is linear and obvious to the reader. Operations happen in their expected order. Resource management becomes trivial. It also allows errors to naturally be more expressive.
This appears to be a decent starting point to further enlightenment: https://davidnix.io/post/error-handling-in-go/ https://davidnix.io/post/error-handling-in-go/
- wvenable 9y ago> I don't think most functions end up having an error result by convention -- many functions never return errors. Only the most trivial functions don't have the possibility of error. And if a function doesn't have an error condition today doesn't mean it won't be changed to have one tomorrow. Java has ruined too many programmers on exceptions by making them checked and making you do a lot of unnecessary try/catch dancing. A program should only have a few key try/catch blocks in the places where recovery is possible and it should be only concerned with what it can recover from. This obsession with every error that any method can throw/return is entirely the wrong way to deal with exceptional conditions. Therefore, error handling in Go is as backwards as it comes.
- lpghatguy 9y agoIf your function doesn't throw errors now, but does in the future, that's an API breakage. Using `Result` types as your only mode of error handling makes that API contract explicit.
- wvenable 9y agoTwo of the fundamental principles of object-oriented programming are encapsulation and polymorphism; the idea that exceptional errors are part of the API contract is completely at odds with idea of isolating the implementation of different components. A component that calculates exchange rates might use a file today and a network service tomorrow. My code should not have to care how every single component is implemented all the way down through the entire code base and every 3rd party library. And if my code is designed to restart high-level operations due to temporary network exceptions then it will be robust without knowing which components use the network now or in the future. Properly typed `result` types and checked exceptions make implementation details explicit. I don't want that part of the API contract.
- cesarb 9y agoWhat matters is whether the component is _fallible_ or not. If a component can fail, its caller has to be able to deal with the failure, even if it deals with the failure by also failing (propagating the failure). Your example already could fail (on a missing file today, on a network problem tomorrow), so a well-designed API contract (which doesn't unnecessarily expose the _cause_ of the failure) wouldn't have to change. The implementation detail is the cause of the failure, not the fact that it can fail.
- wvenable 9y agoAll components can fail; even if just by programmer error. There is a difference between expected failures and unexpected failures. I like to use the example of int.Parse() and int.TryParse() in C#. These functions do exactly the same thing but treat errors very differently. You use int.Parse() when you expect the parse to always succeed. It will raise an exception if the parse fails. int.TryParse() doesn't raise an exception, it returns a boolean and you use it when you expect the parse to fail. Returning a boolean indicating success/failure can be part of the API contract with well defined semantics. But that can't help you with unexpected errors that are part of the implementation. For unexpected failures, you want to know the cause of it and you want as much implementation detail as possible. I can't recover from a missing file, but I can recover from temporary network problems. If the file is missing, I want to know what file is missing so I can fix that manually.
- Will_Parker 9y ago> If your function doesn't throw errors now, but does in the future, that's an API breakage. So what you're saying is that, to avoid future API breakages, you should include an error result in every non-trivial function, and probably the trivial ones too in case a changing spec makes the behavior not so trivial anymore.
- zaarn 9y agoIt's a bit silly to avoid API breakages by always returning a nil error. If a function is changed to now return an error, it's API breakage. I would consider it a breakage if it previously return just nil or not since the previous "always nil" is documented or implicitly contracted as part of the API.