4 ms·
The common "don't use exceptions for control flow" is broken right off the bat with StopIteration. I just think error handling in Python is god awful, really.
by geewee 7y ago
The common "don't use exceptions for control flow" is broken right off the bat with StopIteration. I just think error handling in Python is god awful, really.
That's not an idiom python has ever subscribed to though, it's always subscribed to the "Better to ask forgiveness than permission" - I think there are strengths to both viewpoints, but honestly I think "don't use exceptions for control flow" is more of a convention that a "truth"
- ComputerGuru 7y ago> honestly I think "don't use exceptions for control flow" is more of a convention that a "truth" In absolute terms or when coding on paper, perhaps. But in the real world and if performance even remotely matters, it’s as close to a universal rule all languages end up embracing or turning into creaking hulks of slow code given how exceptions work in practice at the level of cpu execution units. C#/IL/.NET embraced excerpts heavily at around the same time (start of the 2000s) but in time developers (in and out of Microsoft) learned the hard way that it doesn’t scale. With .NET core, exceptions for flow control are completely verboten and APIs have been introduced to provide alternatives where missing. Exceptions should be so rare if you chart all handled exceptions, you shouldn’t see any and would thoroughly explore why one or more pop up when a system or dependency hasn’t exploded.
- momentoftop 7y agoThe Ocaml exception implementation is comparatively very performant, and so its exceptions are routinely used for control flow.
- Rapzid 7y agoInterestingly .Net has very performant exception handling as well. Not sure if something changed internally with Core, but using exceptions in place of return error codes was incredibly common within the .Net ecosystem.
- profquail 7y ago.NET has always had slow exception handling because it tied/ties in Windows’ Structured Exception Handling (SEH); that’s rather slow but provides e.g. detailed stack traces even for mixed-mode callstacks. Having ported some decently large codebases from OCaml to F#, the heavy use of exceptions in OCaml (where exceptions are very lightweight by design) had to be changed to normal control flow with monads to achieve good performance in F# — specifically because of .NET’s slow exception handling.
- 9q9 7y agoI'd be interested to learn about how exceptions are typically using in Ocaml for non-local control flow. Exceptions have two core properties: (1) non-local jump (carrying a value) and (2) dynamic binding of place to jump to. Contrast this with e.g. break / continue in loops where (2) does not hold. If most use cases of performant OCaml exceptions were not making use of (2) that would be an interesting insight for programming language design. Have you got data on this matter?
- momentoftop 7y agoNo data as such. But here's the first example in the "batteries included" list library: let modify_opt a f l = let rec aux p = function | [] -> (match f None with | None -> raise Exit | Some v -> rev ((a,v)::p)) | (a',b)::t when a' = a -> (match f (Some b) with | None -> rev_append p t | Some b' -> rev_append ((a,b')::p) t) | p'::t -> aux (p'::p) t in try aux [] l with Exit -> l Here, the Exit exception is being raised as an optimisation: if no modification happens, then we return the original list, saving an unnecessary reverse. The try is the only place that the Exit exception is caught, so the jump location is static, much like a break.
- 9q9 7y agoThanks. That's, as you say, a statically scoped fast exit. One does not need the full power of exceptions for this (exceptions'd dynamic binding comes with a cost). If exceptions are widely used for this purpose, one might consider adding a nicely structured goto to the language. Something like linear delimited continuations?
- momentoftop 7y agoSorry, I misunderstood what you were after. Ocaml exceptions are used more generally, and often make use of 2. For instance, the basic stream/iterator in Ocaml is Enum, which always uses an exception to signal when the stream has been exhausted, rather than providing a "has_next" predicate. The catch site of this exception is dynamic.
- goto11 7y agoIf you care that much about performance you probably shouldn't use Python in the first place. Python deliberately prioritize ease of development above performance.