4 ms·
If 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
by lpghatguy 9y ago
If 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.