Timeline for Exceptions, error codes and discriminated unions
Current License: CC BY-SA 4.0
46 events
| when toggle format | what | by | license | comment | |
|---|---|---|---|---|---|
| Jun 8, 2018 at 19:57 | comment | added | 8bittree | @T.Sar I am not the one missing the point. Yes, crashing is better than continuing in an uncertain state. I am aware of this, and the question also specifically states this. But, crashing is not good (it can cause additional problems). What is good, is not being in an uncertain state. This question is asking if it makes sense to use a technique (in C#) that brings attention to potential sources of uncertain state and forces you to make a decision on how to handle them. The question acknowledges that this technique won't always work (OOM), but is asking about the many cases in which it does. | |
| Jun 8, 2018 at 12:17 | comment | added | T. Sar | @8bittree You're missing the point. Crashing isn't about not handling anything. It is about stopping the system when it gets to an uncertain state. You can put a lot of error handling inside your code, you can check at compile time, do whatever you want - that doesn't mean you'll cover all the edge cases, and if it fails, well - halt and catch fire. | |
| Jun 7, 2018 at 23:28 | comment | added | 8bittree | @T.Sar Tests usually require running (at least part of) the code. I'm talking about detecting issues during compilation. With discriminated unions, rather than return a File when opening a file, you would return a Result<File, Error>. A Result<File, Error> is not a File, so, if you forget to handle it, and simply try to use it as a File, you'll get a compilation error. Instead, you've got to either check what it contains, and extract the File or Error, or you can postpone the error handling and use methods like map to safely operate on the contained File or noop on an Error | |
| Jun 7, 2018 at 11:32 | comment | added | T. Sar | @8bittree Of course I did, and do that - unit and integration tests are there for that. However, I won't be naive to say that our unit tests will cover all the cases and that the software will never have bugs or crash - more so when we need to integrate our system with a system under control of a third party, like the government or a university. More than that - there are several types of issues you can't detect before the code runs (a problem in the DB, a bad file from the user, a driver issue, etc). | |
| Jun 6, 2018 at 21:55 | comment | added | 8bittree | @T.Sar Have you considered the possibility of detecting the issue before the program ever runs, complete with the exact line number (maybe even character) where it occurs? And since you were probably just working on that code (you do try to compile your stuff once in a while, right?), you can probably figure out some proper handling in closer to 30 seconds than 30 minutes. Seems like that would be the better option than hoping that testing encounters the right situation to trigger the exception and a crash before it escapes into the wild and brings some hapless user's day to a screeching halt. | |
| Jun 6, 2018 at 16:18 | comment | added | hegel5000 | I was under the impression that the oxymoronic "don't use exceptions as control flow" was a holdover from C++, where exceptions are relatively unperformant compared to something like boost::optional (or std::optional, as of recently). You could use exceptions for rare cases such as a hardware error; but not for highly probable 'failures' such as a check for a particular token in a parser (in which case a dozen-or-so checks might 'fail' until a match is found). | |
| Jun 6, 2018 at 15:53 | comment | added | user9993 | This is my preferred approach: it's functional and is in a similar vein to DavidArno's answer: enterprisecraftsmanship.com/2015/03/20/… | |
| Jun 6, 2018 at 11:43 | answer | added | Luaan | timeline score: 1 | |
| Jun 6, 2018 at 11:07 | answer | added | Cephalopod | timeline score: 2 | |
| Jun 6, 2018 at 0:13 | comment | added | Frank Hileman | I think you summarized the thinking well. One person's opinions evolved to an error model more similar to Clu, and I think you would appreciate why it is better: joeduffyblog.com/2016/02/07/the-error-model In such a model, you can use the functional or exception style, but the exception propagation stops at certain boundaries by default. Exceptions are specifically to throw away state and recover; often the entire process is the only thing we can throw away, and recovery requires a new one. | |
| Jun 5, 2018 at 19:52 | comment | added | Alexander | I think we should distinguish between "error codes" like C's errno, and discriminated unions. Perhaps the discriminated union uses a number internally as the tag that denotes which case is active, but that's an implementation detail that a good implementation won't expose to you. | |
| Jun 5, 2018 at 19:23 | comment | added | jrh | @MarkAmery I agree that the terminology is confusing on this, personally I am okay with the borderline "flow of control" case where an API method didn't throw an exception but it returned valid but unusable data, e.g., try{data = apiObj.ReadData(); if(data == null) throw new SomeException("Data contained was null") (...)}catch(SomeException ex){throw new SomeException($"Unable to read data from X: {ex.Message}", ex)} -- if it ends up in the same place anyway, and it conceptually means the same thing (e.g., the file was invalid), may as well just have error handling only in the catch block | |
| Jun 5, 2018 at 11:37 | comment | added | Mark Amery | Throwing an exception literally is a control flow mechanism. The common saying that it shouldn't be done "as control flow" thus doesn't make a jot of sense if taken literally (or at least could be shortened to simply "don't use exceptions"). Of course, people who use the saying don't take it literally; they instead infer what they consider to be a common-sense interpretation of what it means. The trouble is, they almost never spell out precisely what that meaning is... and different users of the saying seem to read different meanings into it. | |
| Jun 5, 2018 at 10:11 | comment | added | mcintyre321 | I think it would be helpful to put a code sample of how you are using OneOf in your question, as I think there is some confusion about OneOf itself being an 'error code'. | |
| Jun 5, 2018 at 10:09 | answer | added | mcintyre321 | timeline score: 7 | |
| Jun 5, 2018 at 9:33 | comment | added | Chrᴉz remembers Monica | It is also worth noting that exceptions wander up the stack until catched while return values are only returned once and then the caller func of the caller func can never get a notice of a error, if the error is not again handled in every calling function of the error-prone function and so on | |
| Jun 5, 2018 at 6:32 | history | edited | Clinton | CC BY-SA 4.0 | edited title |
| Jun 5, 2018 at 6:27 | history | edited | Clinton | CC BY-SA 4.0 | edited title |
| Jun 4, 2018 at 16:55 | comment | added | Theraot | When a method returns void, you do not have a motivation to check the returned value, because you can't, because there is none. If you replace this with a solution based on error code, you may forget as much as you may forget with an exception. Futhermore, .NET exceptions are cheap, there is no runtime cost for a try-catch unless there is an exception. But checking the return value has a cost always. | |
| Jun 4, 2018 at 15:08 | comment | added | supercat | ...the second operation is performed, but this approach can ensure that unhandled errors don't go unnoticed without imposing the cost of exception-handling mechanisms in cases where code is prepared to deal with errors. | |
| Jun 4, 2018 at 15:07 | comment | added | supercat | @jpmc26: One approach I've seen in the days before OOP became popular, and which I rather like, but haven't seen discussed lately is having the first error both latch and return an error code, and requiring that the caller explicitly acknowledge the error before attempting the next operation. Attempting a second operation without acknowledging an error will trigger a trap. One must be careful to balance the goals of ensuring that the cause of the original error isn't lost with possible confusion (or even security issues) that could result by having it reported when... | |
| Jun 4, 2018 at 14:23 | answer | added | maaartinus | timeline score: 4 | |
| Jun 4, 2018 at 14:10 | comment | added | ctrl-alt-delor | C# has not traditionally reserved exceptions, for exceptional circumstances. E.g. it will through an exception for End Of File. Other languages will return an error, or use query methods is_end_of_file, and only throw an exception, if an attempt is made to read beyond the end of the file. | |
| Jun 4, 2018 at 14:10 | comment | added | Clinton | @SørenD.Ptæus I don’t understand why the file opening function just can’t return ‘Option<File>’ to avoid the race condition and the exception. | |
| Jun 4, 2018 at 14:08 | comment | added | Konrad Rudolph | How come none of the answers clarifies that the Either monad is not an error code, and nor is OneOf? They are fundamentally different, and the question consequently seems to be based on a misunderstanding. (Though in a modified form it’s still a valid question.) | |
| Jun 4, 2018 at 14:06 | comment | added | Konrad Rudolph | @SørenD.Ptæus That should be an answer (and it would, incidentally, be a better answer than all the existing ones). | |
| Jun 4, 2018 at 12:34 | comment | added | kutschkem | Your approach sounds like reinventing checked exceptions. With all the benefits and problems, except that you have to crash your program yourself if you can't handle the error. All in all, this is still a lot better than return codes. | |
| Jun 4, 2018 at 12:06 | comment | added | T. Sar | I may be alone on this, but I think that crashing is good. There is no better evidence that there is issue on your software than a crash log with the proper tracing. If you have a team that is able to correct and deploy a patch in 30 minutes or less, letting those ugly buddies show up may not be a bad thing. | |
| Jun 4, 2018 at 11:52 | comment | added | Søren D. Ptæus | Related article: Eric Lippert on the four "buckets" of exceptions. The example he gives on exogenous eceptions shows there are scenarios where you just have to deal with exceptions, i.e. FileNotFoundException. | |
| Jun 4, 2018 at 11:26 | review | Close votes | |||
| Jun 9, 2018 at 3:02 | |||||
| Jun 4, 2018 at 11:00 | answer | added | Frank Hopkins | timeline score: 0 | |
| Jun 4, 2018 at 10:56 | history | tweeted | twitter.com/StackSoftEng/status/1003591418823180288 | ||
| Jun 4, 2018 at 10:56 | answer | added | Aleksandr Dubinsky | timeline score: 4 | |
| Jun 4, 2018 at 10:24 | comment | added | Clinton | @jpmc26: You can fail to handle an exception properly in a catch clause also. I don't see what is lost here. A caller who doesn't want to deal with the errors can just pass them up the stack by returning them. | |
| Jun 4, 2018 at 9:42 | comment | added | jpmc26 | The reminder also has less value if your code is stateless anyway, in which case you don't particularly need a reminder to do anything. | |
| Jun 4, 2018 at 9:35 | comment | added | jpmc26 | Somewhat off-topic, but note that the approach you're looking at has the general vulnerability that there's no way to ensure the developer handled the error properly. Sure, the typing system can act as a reminder, but you lose the default of blowing up the program for failing to handle an error, which makes it more likely that you end up in some unexpected state. Whether the reminder is worth the loss of that default is an open debate, though. I'm inclined to think it's just better to write all your code assuming an exception will terminate it at some point; then the reminder has less value. | |
| Jun 4, 2018 at 9:28 | history | edited | Clinton | CC BY-SA 4.0 | edited body |
| Jun 4, 2018 at 8:36 | comment | added | user155776 | @Clinton - I had asked a similar question a while ago - softwareengineering.stackexchange.com/questions/271127/… | |
| Jun 4, 2018 at 8:31 | history | edited | Clinton | edited tags | |
| Jun 4, 2018 at 8:04 | answer | added | Ewan | timeline score: -2 | |
| Jun 4, 2018 at 7:39 | comment | added | Astrinus | Use F# Result ;-) | |
| Jun 4, 2018 at 7:23 | answer | added | David Arno | timeline score: 26 | |
| Jun 4, 2018 at 3:58 | answer | added | candied_orange | timeline score: 140 | |
| Jun 4, 2018 at 2:54 | answer | added | kevin cline | timeline score: 38 | |
| Jun 4, 2018 at 1:57 | history | edited | Clinton | CC BY-SA 4.0 | added 723 characters in body |
| Jun 4, 2018 at 1:46 | history | asked | Clinton | CC BY-SA 4.0 |