4 ms·
I'm curious as to why you prefer (unchecked) exceptions over "all modern trends to use [the] return value" with sum types like Result (Either in Haskell). Pers
by cies 6y ago
I'm curious as to why you prefer (unchecked) exceptions over "all modern trends to use [the] return value" with sum types like Result (Either in Haskell).
Personally I follow with the trend and feel strongly that the Result-type approach is superior over (unchecked) exceptions in most cases, since: (1) they are much easier to reason about for me as a programmer reading and writing code, as it (2) produces code with easier execution flow and (3) uses the type system to specifically+exhaustively communicate the possible outcomes.
I say "in most cases", as I believe there are (probably) some behaviours that are easier to express using exceptions, but in practice I rarely run into such cases. And when I run into them, I might simply not be able to come up with the right abstraction using Result-types.
I find the Rust/Kotlin trend of only allowing one exception (the panic), as to dictate the language users to use other means to communicate/express multiple outcomes of a call. Both languages --- by having sum types + pattern matching switch statements + exhaustivity checks on switches over sum types --- also provide means to express/handle multiple outcomes of a call, thus rendering the prevalent use of exceptions mostly irrelevant.
The hierarchy of exceptions that you mention are to me something that goes against the nature of typing discipline: they subvert the typing system (in case of unchecked) or add a additional typing component (in case of checked) to express/handle multiple outcomes of a call.
- jcelerier 6y ago> they are much easier to reason about for me as a programmer reading and writing code, I would hate to have to reason about the 250 possible errors coming from every single library underneath my app, especially when the immense majority of those are better left to be solved by the user (e.g. file cannot be opened because the usb stick was removed, audio device unavailable because you are using a pro audio driver that locks to a single app, the GPU cannot be opened because I did a driver update and must reboot, etc etc). Monadic error handling is fine when you have a finite, predetermined set of "things that can go wrong" - e.g. you're writing a small input parser for a text field, either it parses or it does not. But imho this is a microscopic amount of cases compared to all the "random stuff that can go wrong" in the average GUI app. And as a GUI app user, I really really hate when the app tries to do what's best for me - autoreconnect, write to another file, change the settings automatically, whatever. Just gimme an error popup and let me decide. > produces code with easier execution flow In C++ using RAII objects for state makes that concern pretty much disappear imho. Save your state & rollback in a tiny type's ctor/dtor and you're good to go, whatever you do, things can throw and what you were doing will be rolled back. Not as good as hardware transactional memory, sure, but apparently not a lot of CPU engineers are able to make that one work yet... > (3) uses the type system to specifically+exhaustively communicate the possible outcomes. back to 1, this is what I really do not want. Just write the happy / known-to-the-programmer path and let what you don't know about bubble up to main() or the top of your event loop.
- masklinn 6y agoThe application use-case is basically what `Box<dyn Error>` (and anyhow which is very much intended as a better version thereof) is for. If you're writing an application and don't really care about the error types, just use `anyhow::Result` (or typedef your own `Result<T, Box<dyn Error>>` to avoid a dependency) as your error type and bubble everything up to main. Then if there are cases where you want to handle specific errors… you can still do that, easily, with the help of the compiler.
- jcelerier 6y agothat's a lot of characters added for essentially the same result - exceptions were introduced because bubbling things up explicitely is a pain !
- masklinn 6y ago> that's a lot of characters added Not really. > for essentially the same result With the huge differences that 1. it signals that things can fail, which largely doesn't exist with unchecked exceptions unless you re-add that information which is going to lead to similar amounts of "characters added" (e.g. “throws”) 2. you're explicitly opting into the costs and information-discarding effects, which is rather important for the language's purported goals, it supports that use-case without discarding its other important use-cases > exceptions were introduced because bubbling things up explicitely is a pain ! Not really either, exceptions were introduced to structure error handling rather than use ad-hoc and generally in-band methods, or unrestricted jumps. They also provided a very useful hook point to provide contextual information (by default or otherwise) which the aforementioned ad-hoc methods didn't account for e.g. not much contextual data you can bundle in a shell or C error code. Leveraging sum types for error handling is an oddly recent development, both because most languages don't have sum types, and because that it could be used thus doesn't seem like a very old realisation at large (just look at error handling in Haskell).
- saurik 6y ago
- vbezhenar 6y agoI prefer unchecked exceptions, because I prefer "crash it" approach to software design. Most of errors should crash my program (or part of it), write detailed logs (including stacktraces) and then it should be restarted, continuing its operation. If it's request-handling system, current request should be aborted. With this approach, error handling is pretty minimal. All I have to think about is exception-safety. And that's an easy thing to reason about, when language does not do manual memory management. I understand that this approach is not suitable for every software field. If I'm writing martian rover software, I don't want to crash my software, when rover might be physically destroyed. Of course some exceptions are not exceptional and they are part of normal flow. But those situations are rare and try-catch is perfectly fine to handle those errors as well. I'm not forced to handle them, that's right. But even if I did not think about it, I'll get crash, I'll get logs, I'll get user complaints and I'll implement that separate handling. Probably it's corner-case anyway, so that's not a big deal. And a big plus is that I don't have to think about exceptional situations when I'm writing my code. That reduces cognitive load and allows me to focus on main logic, rather than thinking about every possible outcome of every called function. And main logic is why I'm writing a software in the first place. Unchecked exceptions are untyped indeed. They are like JavaScript on top of typed language. Well, I'm not strict-type purist. I'd use Haskell if I were one, I guess. Dynamic typing is popular for a reason. May be it would be an interesting project to implement strictly-typed exception handling. But Java did it wrong. I don't know how to make it right. If someone would make strictly-typed exception handling natural and friction-less to use, I'm all for that. But I did not see that kind of a system anywhere. And untyped works for me good enough.
- thayne 6y agoBut that is part of the appeal of errors as part of the return. Recoverable errors are just return values and can be handled with normal code, and irrecoverable errors panic.
- ben-schaaf 6y agoThat's pretty much exactly what Rust does. You sprinkle unwrap where you don't care or don't expect failure and it will crash there (with a backtrace), otherwise you can use the better error handling semantics of Result to properly handle the errors you care about.
- higerordermap 6y agoIt depends on what kind of applications one is writing. In many applications errors are dispatched to some common handler far above the call stack. This is also the justification for uncheked exceptions in C# given by Anders Hejlsberg.
- saurik 6y agoHaskell has monads and syntax for monads. Rust doesn't. Do not mix the two together as if one justifies the other: as someone who prefers monads to exceptions (which are essentially a hardcoded syntax for a specific error handling monad), Rust's non-monadic manual error checking paradigm feels ridiculous.