This is a discussion on Python’s forums about adding something akin to a throws
keyword in python.
@sugar_in_your_tea I’m by far not qualified to discuss this in depth. But it seems to me that almost every function call ever can fail. Therefore, do you need to do this with every single function call?
That seems terribly inefficient and bloated. How is that readable for anyone?
That’s where the difference between exceptional cases comes in. Rust and Go both have the concept of a panic, which is an error that can only be caught with a special mechanism (not a try/except).
So that’ll cover unexpected errors like divide by zero, out of memory, etc, and you’d handle other errors as data (e.g. record not found, validation error, etc).
I don’t think Python should necessarily go as far as Go or Rust, just that handling errors like data should be an option instead of being forced to use try/except, which I find to be gross. In general, I want to use try/except if I want a stack trace, and error values when I don’t.
@sugar_in_your_tea But isn’t all that possible in Python? Don’t monads cover exactly what you want? Why does it need to be implemented some different way?
Also, divide by zero should be data just as well. Failing to program around having nothing to divide by is not a reason to have a program panic.
Also, having two systems for largely the same behavior doesn’t seem to improve usability and clarity, in my opinion.
divide by zero should be data as well
I disagree. You should be checking your input data so the divide by zero is impossible. An invalid input error is data and it can probably be recovered from, whereas a divide by zero is something your program should never do.
If having the error is expected behavior (e.g. records/files can not exist, user data can be invalid, external service is down, etc), it’s data. If it’s a surprise, it’s an exception and should crash.
doesn’t seem to improve usability
I’m proposing that the programmer chooses. The whole design ethos around Python is that it should look like pseudocode. Pseudocode generally ignores errors, but if it doesn’t, it’s reasonable to express it as either an exception or data.
Documenting functions with “throws” isn’t something I’d do in pseudocode because enumerating the ways something can fail generally isn’t interesting. However, knowing that a function call can fail is interesting, so I think error passing in the Rust way is an interesting, subtle way of doing that.
I’m not saying we should absolutely go with monadic error returns, I’m saying that if we change error handling, I’d prefer to go that route than Java’s throws, because I think documenting exceptions encourages bad use of exceptions. The code I work on already has way too many try/except blocks, I’m concerned this would cement that practice.