Just because python uses exceptions for everyday control flow doesn’t mean it’s a good idea for you to do so as well. I like python. It’s probably these days my favourite scripting language. That doesn’t mean I think it’s decision to use exceptions like that in it’s API was a good idea.
Why is it not a good idea? They have some pretty convincing examples that fighting the exception handling convention is unwise, your counterpoint seems to be “No it isn’t”. Could you elaborate?
While I may have to handle the exceptions that their API’s are throwing that doesn’t mean I have to throw my own exceptions in my own API’s. I use exceptions for errors indicating invalid state where there is nothing good that can be done.
I don’t have to fight the convention in the python stdlib api’s or libraries that I don’t control since the battle was already decided.
However if I’m designing my own library or API I am free to choose differently.
(value, True) is a disingenuous example. This isn’t go, we’re in a real OO programming language and can use polymorphism to force proper error handling while still returning ordinary values (i.e. offer the equivalent of Option#fold)
My extensive Scala experience is that it’s actually perfectly possible to wrap at the boundaries (when calling libraries that use exceptions) and use Maybe-like techniques within your own code.
Exceptions do not protect you from having to think about race conditions. The examples in the article perpetuate the dangerous illusion that any line of Python executes atomically.
So what’s the alternative? If I want a lightweight, readable language and I can’t stand exceptions, Python may well still be my best option.
One smart co-worker once taught me a simple thing: human brain is the most adaptive system of all that we use in software. In subjective cases where you simply “can’t stand” something the alternative is to change the attitude rather than work against any piece of software. It’s usually much easier than it seems at first.
(Trying Rust after ~10 years in Python, it seemed to me at first that there’s no way people writing all this error matching code couldn’t see that it’s awfully inefficient and unreadable. When I switched my brain from fighting into understanding, it stopped hurting in about two weeks time.)
I felt the same way about Go (having to write all the error handling code drove me nuts). After a little bit I didn’t mind at all. It’s a design choice and it fits with the language’s design. Go’s panic/recover provides truly exceptional exceptions as the author of this post seems to prefer (though they’re not declared).
But generally this post doesn’t make much sense to me. Maybe the author’s actually annoyed by the difference between checked and unchecked exceptions?
One insight for me from this post was that Python is possibly the best implementation of exception based error handling. I’m not ready to properly defend this position though, it’s just something to think about.
OCaml with Core as your stdlib is actually pretty solid. All methods that return exceptions are explicitly marked by ending in _exn, and I can’t think of any example where there isn’t an equivalent version that instead returns Some foo or None, which (unlike in Go) you cannot ignore and must pattern match sanely. This post and its ancestors provides a good comparison of Python and OCaml from the perspective of moving a very large code base from one language to the other incrementally.
Sure (and in reality I personally use Scala which is pretty similar to OCaml). But I don’t think it’s entirely unreasonable to prefer Python to OCaml, even if one dislikes exceptions - the OCaml syntax is a bit ugly, Python’s OO support is actually quite nice, and while OCaml has become a lot more popular of late there’s still a bigger community/ecosystem around Python, particularly for the web or science.
What about exceptions do you not like? Do you mean you want to pursue complete correctness? Do you prefer to have things silently fail? I’m genuinely curious, and I can’t sympathize.
I find exception control flow confusing to read, and hard to retain correctness when refactoring. I prefer to use values (functional style) for any “normal” control flow e.g. input validation, no entries found (where that’s a part of normal operation), any “4xx situation”. I’d only use exceptions for “system” failures like out-of-memory, connection refused, a “5xx situation”, or anything that definitely indicates programmer error - the kind of thing I’d expect to set up developer alerts for.
I find exception control flow confusing to read, and hard to retain correctness when refactoring.
This is probably, full stop, the worst thing about dealing with “exception first” languages.
I find it impossible to refactor code that generates / handles exceptions the first time. I almost always end up with the exception handler too deep to react, or the exception generator moved above the scope of the catch statement.
Say what you will about Java, but at least the compiler yells at me when that happens. In Python I have to depend on my tests to tickle the condition – and if it’s truly an exceptional exception, the odds of that error sneaking into my code is significantly high.
I was just having an extended exchange about this on HN. Even in Java with a checked exception, I’ve broken working code by reordering seemingly orthogonal lines. I think you need a (small) visual distinction between calls that throw and calls that don’t - something like the =/ distinction you get with monadic style. Alternatively, if all the Java tools highlighted calls that declare exceptions (the same way implicit conversions or by-name parameters get a green underline when working in Scala) that would make checked exceptions a lot more usable.
Once you have exceptions, you have to start thinking about exception safety. How hard that is depends on the language.
Exceptions can be seen as dishonest, in a sense – they are an affordance that encourages a “fix and continue” style of programming, and where they are available, they often are used for things that are non-fatal, hence, non-exceptional. I don’t mind them as much as some others do, and I do sympathize with the notion of not fighting the platform – yes, you could implement a Some 'a | Error of msg idiom in Python, but you’d still be stuck with catching and wrapping exceptions, so you’re better off just going along to get along.
Some 'a | Error of msg
Lua seems like an obvious choice; it’s several orders of magnitude lighter, just as readable, and doesn’t have exceptions.
error/pcall are Lua’s throw/catch, they just don’t get used much.
While the author’s point is reasonably well-argued, it does not address my major gripe with exceptions in python: the 3am exception. Anyone writing long-running python code will doubtless run into this at some point. The problem is that it is impractical to inspect every method called by the method I’m calling to see what exceptions could possibly be raised. This leads to the program grinding to a halt until a dev can inspect the stack trace and add the appropriate exception handling. Maintaining checkpointing discipline means that work already completed won’t be lost, but the potentially long time between the crash and the discovery of the crash is nonetheless lost.