4 ms·
Hard disagree. Exceptions are actually good. They make code clear and errors hard to ignore. I've written a ton of code over decades in both exceptional and exp
by quotemstr 2y ago
Hard disagree. Exceptions are actually good. They make code clear and errors hard to ignore. I've written a ton of code over decades in both exceptional and explicit-error languages and I'll take the former every day. There's no function color problem. No syntactic pollution of logic with repetitive error propagation tokens.
Also, exception systems usually come with built in stack trace support, "this error caused by this other error" support, debugger integration ("break the first time something goes wrong"), and tons of other useful features.
(Common Lisp conditions are even better, but you can't have everything.)
You can't just wave the word "goto" around as if it were self-evident that nonlocal flow control is bad. It isn't.
> And nearly always, after an exception is “handled”, the application is actually in an unknown state and cannot be reasoned about.
That's not been my experience at all. Writing exception safe code is a matter of using your language's TWR/disposable/RAII/etc. facility. A programmer who can't get this right is going to bungle explicit error handling too.
Oh, and sum types? Have you read any real world Rust code? Junior developers just add unwrap() until things compile. The result is not only syntactic clutter, but also a program that just panics, which is throwing an exception, the first time something goes wrong.
Many junior developers struggle with error handling in general. They'll ignore error codes. They'll unwrap sum types. They might... well, they'll propagate exceptions non-fat ally, because that's the syntactic default, and that's usually the right thing. We have to design languages with misuse in mind.
- OtomotO 2y agoBah, no, I hated that you had to wrap basically every code block in a try/catch in Java, because the underlying lib could change and suddenly throw a Runtime-Exception. At the same time Checked Exceptions were a nightmare as well, because suddenly they were part of the contract, even though maybe wrong later.
- quotemstr 2y agoChecked exceptions are more trouble than they're worth. That doesn't make exceptions in general bad.
- OtomotO 2y agoAs said, I don't like the wrapping of about everything with try/catch Sure, you can only do it way up the stack, but that's not enough quite often. If you can only do it all the way up, I find it ergonomic. Maybe I should experiment more with catch unwind in Rust.
- Cthulhu_ 2y agoWhat does make exceptions bad in my opinion (and shared by Go developers?) is a few things: 1. Exceptions are expensive (at least in Java / C#), as they generate a stack trace every time. Which is fine for actually exceptional situations, the equivalent of `panic()` in Go, but: 2. Exceptions are thrown for situations that are not exceptional, e.g. files that don't exist, database rows that don't exist, etc. Those are simple business logic cases. The workaround is defensive coding, check if the file exists first, check if the row exists? that kind of thing. 3. The inconsistency between checked and unchecked exceptions. 4. Distance - but this is developer / implementation specific - between calling a function that can throw an error and handling it. But #2 is the big one I think. Go's error handling is one solution, but if it's about correct code, then more functional languages that use the Either pattern or whatever it's called formally are even better. Go's approach is the more / most pragmatic of the options.
- cesarb 2y ago> e.g. files that don't exist, database rows that don't exist, etc. [...] The workaround is defensive coding, check if the file exists first, check if the row exists? Ugh NO. Please don't. You should never "check if the file exists first". It can stop existing between your check and your later attempt at opening the file (the same with database rows). That can even lead to security issues. The name for that kind of programming mistake, as a vulnerability class, is TOCTOU (time-of-check to time-of-use). The correct way is always to try to do the operation in a single step, and handle the "does not exist" error return, be it a traditional error return (negative result with errno as ENOENT), a sum type (either the result or an error), or an exception.
- OtomotO 2y ago
- didntcheck 2y ago> the underlying lib could change and suddenly throw a Runtime-Exception. And what would you do in that case? Since this is a future change your existing code presumably wouldn't know what else to do but throw its own exception, so why not just let that one propagate?
- OtomotO 2y agoAlways depends on which level it happens
- maleldil 2y ago> Have you read any real world Rust code? Junior developers just add unwrap() until things compile. If you really don't like unwrap[1], you can enable a linter warning that will let you know about its uses to flag it during code review. You know exactly where they are and when they happen. Exceptions are hidden control flow, so you rely on documentation to know when a function throws. > Writing exception safe code is a matter of using your language's TWR/disposable/RAII/etc. facility. A programmer who can't get this right is going to bungle explicit error handling too. Rust has RAII, so you don't have to worry about clean-up when returning errors. This is a Go problem, not Rust. [1] https://blog.burntsushi.net/unwrap/ https://blog.burntsushi.net/unwrap/
- biorach 2y ago> Oh, and sum types? Have you read any real world Rust code? Junior developers just add unwrap() until things compile. Junior developers will write suboptimal code in any language. So I'm not sure what your point is.