Live data from Hacker News

My philosophy of exceptions: they're always ambiguous (2021)

adamhooper.medium.com

11–20 of 46 posts

Re: My philosophy of exceptions: they're always ambiguous (2021)

#11
post #2

> So you think you know what an “exception” means? An exception is when code can no longer do any meaningful work. This could be because of a programming bug, unexpected input, network issue, hardware issue, etc. The cause doesn't really matter. What matters is that there is no way forward for the code. And this is a decision that is made (or not made) by the programmer. In this author's Python example, he chooses ac…

> An exception is when code can no longer do any meaningful work.

That is quite obviously not true, not even on the surface. I have written tons of code where I've caught exceptions and then continued to do meaningful work.

The author's Python example even contradicts your statement. Sure, maybe they should validate the URL first. But even if they did that, the server could be down, the user's internet could be down, or there could be a typo in the URL that makes it unreachable, even if the URL itself is still valid.

And that doesn't have to mean the code can no longer do meaningful work. Maybe the exception handler could consult a local disk cache, and read the contents of the cache and display that instead.

I think exceptions could (and perhaps should) only be used when code can no longer do any meaningful work, but in practice that is very much not how real-world software works.

Ultimately, though, I prefer languages that don't have exceptions. Errors should be passed as return values, preferably using language features that make it impossible to forget to handle them. If they truly get into a state where they can't (or it's unsafe to) do more meaningful work, then they should abort() and the program should be terminated.

Re: My philosophy of exceptions: they're always ambiguous (2021)

#12
post #11
post #2

> So you think you know what an “exception” means? An exception is when code can no longer do any meaningful work. This could be because of a programming bug, unexpected input, network issue, hardware issue, etc. The cause doesn't really matter. What matters is that there is no way forward for the code. And this is a decision that is made (or not made) by the programmer. In this author's Python example, he chooses ac…

> An exception is when code can no longer do any meaningful work. That is quite obviously not true, not even on the surface. I have written tons of code where I've caught exceptions and then continued to do meaningful work. The author's Python example even contradicts your statement. Sure, maybe they should validate the URL first. But even if they did that, the server could be down, the user's internet could be down,…

> But even if they did that, the server could be down, the user's internet could be down, or there could be a typo in the URL that makes it unreachable, even if the URL itself is still valid.

That doesn't contradict my statement. In all those cases, the code cannot continue to do anymore meaningful work. Handling the parsing error just handles that one situation.

> I have written tons of code where I've caught exceptions and then continued to do meaningful work.

But the code that threw the exception could not continue to do meaningful work. You can the catch the exception and do something else or maybe try the same operation again. But either way, the original code could not and did not continue. In general, you have to reach higher up in the call stack before you find a spot where you can something different or retry. This is why exceptions are useful.

Re: My philosophy of exceptions: they're always ambiguous (2021)

#13
post #7

My wish: people stop using the dumb line that exceptions are gotos.

The pattern for returning errors is something like the following pseudocode: main() { (result or error) = functionA(); if (error) die("There was an error: " + error); print result } functionA() { (result1 or error1) = functionB() if (error1) return error1; (result2 or error2) = functionC(result1); if (error2) return error2; (result3 or error3) = functionD(result2); return result3; } functionB() { (result or error) =…

[deleted]

Re: My philosophy of exceptions: they're always ambiguous (2021)

#14
post #11

Earlier quoted context omitted.

> An exception is when code can no longer do any meaningful work. That is quite obviously not true, not even on the surface. I have written tons of code where I've caught exceptions and then continued to do meaningful work. The author's Python example even contradicts your statement. Sure, maybe they should validate the URL first. But even if they did that, the server could be down, the user's internet could be down,…

> But even if they did that, the server could be down, the user's internet could be down, or there could be a typo in the URL that makes it unreachable, even if the URL itself is still valid. That doesn't contradict my statement. In all those cases, the code cannot continue to do anymore meaningful work. Handling the parsing error just handles that one situation. > I have written tons of code where I've caught except…

> That doesn't contradict my statement. In all those cases, the code cannot continue to do meaningful work. Just fixing the parsing error just takes one such condition out. I never claimed otherwise.

If you continued reading to the next paragraph, I presented an option for how the code could continue doing more work.

> But the code that threw the exception could not continue to do meaningful work. You can the catch the exception and do something else or maybe try the same operation again. But either way, the original code could not continue.

But that's not what you said! Or at least what you said was ambiguous. "The code can no longer do any meaningful work." What code? I interpreted that as "all the following code", which feels like an entirely reasonable interpretation to me. I feel like this just proves the author's point -- it's ambiguous!

Ok, so then what you really mean is: "the code that threw the exception can no longer do any meaningful work". Ok, sure, that's true, but I don't think that's a useful definition. I suppose perhaps it's useful if you're explaining it to a non-programmer who is unlikely to need any more information (arguably, you shouldn't bring up exceptions to a non-programmer; just tell them "the code encountered an error and couldn't continue anymore"). But if you're talking to a programmer, or, rather, teaching a new programmer, that definition is entirely insufficient. Because from there, you need to talk about why the way to signal that inability is to use an exception. You need to talk about other ways to signal that, and what the trade offs are. You need to dig deeper. At best your definition is only the very very very beginning of a longer conversation; it doesn't give enough information on its own.

Re: My philosophy of exceptions: they're always ambiguous (2021)

#15
post #14

Earlier quoted context omitted.

> But even if they did that, the server could be down, the user's internet could be down, or there could be a typo in the URL that makes it unreachable, even if the URL itself is still valid. That doesn't contradict my statement. In all those cases, the code cannot continue to do anymore meaningful work. Handling the parsing error just handles that one situation. > I have written tons of code where I've caught except…

> That doesn't contradict my statement. In all those cases, the code cannot continue to do meaningful work. Just fixing the parsing error just takes one such condition out. I never claimed otherwise. If you continued reading to the next paragraph, I presented an option for how the code could continue doing more work. > But the code that threw the exception could not continue to do meaningful work. You can the catch t…

> What code? I interpreted that as "all the following code", which feels like an entirely reasonable interpretation to me.

It is. As long as you define "all following code" as the code that would execute from that point forward. If you define "following" as simply the code in the rest of the file then doesn't really make sense in this context I don't why you would mean that.

Obviously the existence of catch statements allows the program to continue from that point but none of that code is following from where the error was (unless you call it again).

> At best your definition is only the very very very beginning of a longer conversation; it doesn't give enough information on its own.

If you want me to write an essay on this, I have actually already written one. But I still think this definition is a good one. It is simple but that's what makes it useful. The entire subject of error handling cannot be distilled into a single sentence definition and that is not what I intended to do.

Re: My philosophy of exceptions: they're always ambiguous (2021)

#16
> Python, C++, Ruby and Java all use exceptions. To them, “exception” means, “nifty piece of syntax kinda like goto.”

Not Ruby. In idiomatic Ruby^ exceptions are for exceptional cases, not code flow. "nifty piece of syntax kinda like goto." is the much underused but very powerful catch+throw.

Or rather, it's a stack unwinding mechanism: think baseball, the catcher expects a ball, with the ball being a message to be passed. Upon that you could implement a specialized and more costly case of stack unwinding which does what fail/raise+rescue does, but catch+throw is more general than exceptions.

Take Python's StopIteration as an example: there's absolutely no need for this to be a full exception, instead it's like `break`ing a loop but across method calls.

^ Nevermind Rails practices. Rails is not Ruby.

Re: My philosophy of exceptions: they're always ambiguous (2021)

#17
post #2

> So you think you know what an “exception” means? An exception is when code can no longer do any meaningful work. This could be because of a programming bug, unexpected input, network issue, hardware issue, etc. The cause doesn't really matter. What matters is that there is no way forward for the code. And this is a decision that is made (or not made) by the programmer. In this author's Python example, he chooses ac…

FWIW I didn't particularly like the article, but I really liked your comment. I think "an exception is when code can no longer do any meaningful work" is a great definition, and also explains why flow control with exception handling works the way it does. Obviously there are gray areas re what it is to mean "code can no longer do any meaningful work", which is why there are decisions to be made about when to throw an…

> I think "an exception is when code can no longer do any meaningful work" is a great definition,

I'm not so sure: that definition glibly turns every error or failure into an exception.

Is "Failure to Open File" really an exceptional condition? This is a common condition that is basically a business rule[1].

Think about the "saving a file" action. There's errors that you expect to happen, and have to provide code to handle (like, permission denied, path exists and is a directory, etc), and exceptional conditions that should never have arisen in a program which no sane person would try to handle (Out of memory, disk write errors, etc).

In both cases, no progress can be made and the callee has to return to the caller. But, in the error cases a well-written program will defend against the errors in a meaningful manner, whereas in the exceptional cases there is literally no point in putting in code to handle those cases.

Just some food for thought - I'm not particularly married to these definitions.

[1] Here are some hypothetical options for failing to open a file:

1. If the file doesn't exist, prompt the user asking if it must be created, and proceed only if "Yes" is received, return to caller if "No" is received.

2. If the file is a directory, display a fatal error message and return to caller.

3. If permission is denied, display an ACL error message and return to caller.

Re: My philosophy of exceptions: they're always ambiguous (2021)

#18
I think this topic has been discussed countless times, and this post fails to add anything worthy to it.

My two cents: use result types, or their analog in your language for expected error conditions, and use exceptions for exceptional situations. Example for the former is parsing, which has an expected failure mode - something couldn’t be parsed as intended. Example for the latter is a network issue during an API call/writing a file/etc. Nonetheless, they are a homologous structure to a Result type in more FP languages, so it’s purely which is a better fit for the given circumstance.

I would also like to defend exceptions, as they are a very good construction with sane and safe defaults. They let people concentrate on the actual business logic, not sprinkling little bit of business and error handling logic all over the place; they do the sane thing by default: auto-unwrap the correct result, auto-bubble up if not handled at a given scope; and often unappreciated: they allow as fine- or course-grained error handling as needed, while it is only possible with some less than readable Monad magic with Result types. And most importantly, they never let an erroneous condition silently go unnoticed, you have to deliberately ignore them for that. With errno checking (c, go), and a bit less so with result types you can fall victim to that.

Edit: Oh, and they add stacktraces! That’s also a very important and useful feature of them, much more so than “grepping for this hopefully unique error code in the codebase to find where it might come from with zero further detail”.

Re: My philosophy of exceptions: they're always ambiguous (2021)

#19

Earlier quoted context omitted.

FWIW I didn't particularly like the article, but I really liked your comment. I think "an exception is when code can no longer do any meaningful work" is a great definition, and also explains why flow control with exception handling works the way it does. Obviously there are gray areas re what it is to mean "code can no longer do any meaningful work", which is why there are decisions to be made about when to throw an…

> I think "an exception is when code can no longer do any meaningful work" is a great definition, I'm not so sure: that definition glibly turns every error or failure into an exception. Is "Failure to Open File" really an exceptional condition? This is a common condition that is basically a business rule[1]. Think about the "saving a file" action. There's errors that you expect to happen, and have to provide code to…

I have a desktop application with a single error handler at its event loop. All that handler does is show the exception message in a messagebox and then the event loop resumes. This application has a "save file" option.

In this application it doesn't matter if the permission is denied, the path exists and is a directory, it was a network share that went away, or a disk write error, or out of memory, or a bug. If I press "Save" and the exception occurs, I can read the message and correct the problem if possible, and then just press Save again. It is incredibly robust and has effectively no error handling.

Of course, that doesn't handle all the cases that you describe. For example, for your case #1 I think that isn't a case that no more meaningful work can be done and isn't an error. Instead, you're just asking a question and then proceeding or not based on the answer.

Re: My philosophy of exceptions: they're always ambiguous (2021)

#20
post #4

Earlier quoted context omitted.

In .NET, they have 2 sets of parsing functions, "Parse" and "TryParse" for parsing strings into other types. The first throws an exception if it can't parse and the second returns a Boolean indicating success or failure. Which function of these you use as a programmer is dependent on the context and it indicates your expectation. You might use "Parse" if you reading a well-defined file format where the string will al…

Was this a workaround for frequent exception handling being too expensive on some platform? I like how Java lets some exception instances be reused without stack frames, so performance issues are less likely to compromise API design.

It, according to Eric Lippert, was at least partially to fix the problem of vexing exceptions[1] that plagued Int32.Parse. Parse was first and it was kind of an unfortunate design decision to make a almost expected result (parsing to fail) throw an exception.

It's worth noting that nowadays Parse is just a wrapper around TryParse that throws (although this is an implementation detail)

[1]: https://ericlippert.com/2008/09/10/vexing-exceptions/

Post reply on HN