6 ms·
I don't know Rust at all but what you're talking about sounds equivalent to (much maligned) Java's checked exceptions system? I never understood the hatred ch
by abraxas 7y ago
I don't know Rust at all but what you're talking about sounds equivalent to (much maligned) Java's checked exceptions system?
I never understood the hatred checked exceptions received especially from the younger crowd. I still write Java at work and I still use checked exceptions whenever they indicate an error condition that must not be ignored by the client code. Many new to the project developers hate me for it initially because they can't just "roll out a feature" without being forced to take care of the corner cases but over time they love the discipline that's imposed on them by checked exceptions.
- hocuspocus 7y ago> I don't know Rust but it sounds like equivalent to much maligned Java's checked exceptions? Ergonomics are important. Chaining calls to functions that return a monadic Result/Either type is easier and neater.
- deleted 7y ago[deleted]
- svieira 7y agoThe problem with checked exceptions as implemented in Java is that not all exceptions are checked, but all error handling uses the same mechanism. Midori nicely separated panics (programmer or system errors) from exceptions. Panics _could not be caught_ - they crashed the process. (Processes were therefore really cheap in Midori). Java, on the other hand, put common programmer errors into the exception category in order to make it easier to work with common code (e. g. `IndexOutOfBounds` should be checked or impossible, but arrays are so common, they decided not to require it and they needed to be backwards compatible so they couldn't make array access return a `Result`-style object). That said, using checked exceptions well is a great aid to making the code base understandable!
- svieira 7y agoAnd for those who are interested in this kind of thing, check out Zig's return traces (a kind of inverted stack trace) for other good ideas on how to do exceptions in a new way: * https://ziglang.org/documentation/master/#Error-Return-Traces https://ziglang.org/documentation/master/#Error-Return-Trace... * https://ziglang.org/#A-fresh-take-on-error-handling https://ziglang.org/#A-fresh-take-on-error-handling
- vnorilo 7y agoMidori's really interesting. I'll just leave the link to Joe Duffy's blog on the Midori error model: http://joeduffyblog.com/2016/02/07/the-error-model/ http://joeduffyblog.com/2016/02/07/the-error-model/
- abraxas 7y agoAgreed that the java runtime has made some high visibility yet nonsensical decisions on when to use checked vs unchecked exceptions. I'd even advocate for doing away with the latter though the recent trend has been towards abandoning the former. I advocate for junior devs to start with the assumption that a new exception should be checked and only consider making it unchecked in specific circumstances.
- oconnor663 7y agoI haven't written very much Java, but here are some differences between Java checked exceptions and Rust errors as I understand them: In Java, a function may throw a long list of checked exceptions, and these lists tend to grow to inconvenient sizes in larger programs. For example, if foo() calls bar() and baz(), which each throw 3 different exception types, then now foo() might throw 6 different exception types. In contrast in Rust, each function can only return 1 error type. If a function needs to represent several different types of internal errors, then the crate that it's in needs to define an error enum type with a variant for each of those. (Either that, or the function can use a generic wrapper error that can contain anything. This is less common in library code but pretty common in application code.) This shifts a lot of work from library callers to library authors, which is a good thing. Someone with more Java experience will need to correct me here: I think it's fairly common to bulldoze all that complexity by declaring a function that just "throws Exception". That saves you from writing out N different types (and more importantly, from changing every transitive caller when a low level library introduces a new exception type). But it kind of defeats the purpose of checked exceptions, by throwing away all the info they provide. It's a shame that you have this "all or nothing" choice when it comes to exceptions and how much complexity you want to deal with. In contrast in Rust, wrapper errors can define automatic "From" conversions from the lower level error types they wrap, and the standard `?` operator automatically applies those conversions. That means that in many cases, a low level library adding a new error variant might not require any changes in its callers at all. The new information is there for callers who want to look for it, but existing abstractions around the error type generally just keep working.
- abraxas 7y agoThis is kind of true but not quite. If you don't want to deal with any exceptions in your function you can declare that it just throws java.lang.Exception or put a verbose list of exception types in your method signature. On the other hand, you're also free to catch and process some and only propagate a subset outside of the function or even wrap the ones you want to process and throw a different exception type from within your function that wraps the existing ones. It's not mandated but pretty idiomatic in Java that any Exception type you define should be able to accept a different exception as its 'cause' in your exception's constructor signature.
- weinzierl 7y ago> I never understood the hatred checked exceptions received especially from the younger crowd. I worked with Java professionally for several years and in a sense I feel the same: Checked exceptions are not as bad as they are often portrayed. At the same time they are certainly not as nice as text book examples make them seem to be. For me the biggest downside always has been that their types are part of the method signature and therefore whenever you change the exception type of one class you more often than not end up refactoring half of your other classes too.
- barrkel 7y agoThe failure mode of a module is a function of its implementation. Beyond simple argument and state validation, interesting failure modes are usually abstraction violations. The tension between needing to communicate the actual reason for a failure (which violates abstraction) and preserving the abstraction (which demands shoehorning error states into some kind of polymorphic receptacle) is at the heart of the problem with checked exceptions. If you try and pass through the failure mode, then new implementations cause new failure modes, which means methods grow new exceptions, which in turn break downstream dependencies, because adding a new exception to the throws clause is a breaking change. If you try and abstract away the differences in failure mode (e.g. with a specific module exception), then you fill the code with boilerplate wrappers, need to translate exceptions at module boundaries, and (in the worst case) invent taxonomies into which all future implementations must awkwardly categorize their failure modes. Specific to Java, there's another bug. The type system can express sum types only for concrete exceptions and not for generics. That means that if you parameterize code with other code (e.g. with a lambda or callback), the argument code can only throw pre-defined or runtime exceptions; there's no way to declare, in the type system, the transitive set of exceptions across control flow when the set of exceptions is determined by the argument. E.g. if you have a generic map(f) method, you can't generically declare that map() throws whatever f() throws without forcing f() to only throw a single checked exception - generic type arguments can't be the sum types that Java permits in throws and catch clauses. All this is costly. Meanwhile, in most actual applications, exception handling is extremely rare; exceptions are almost always propagated up to a top-level handler and logged. When exceptions are handled, it's usually near the leaves of the control flow graph, where the application interacts with other systems, like the network or the file system; these inherently non-deterministic failures often need explicit management. So the work to propagate the exception types throughout the control flow graph is pointless.
- calessian 7y agoSee, having reasonable typed exceptions is perfectly acceptable, but that's rarely been the reality I encountered. About every method in our code base has the same 3 checked exceptions in the declaration, and none of them are handled - they're even passed to service clients. And it's just super clunky, but the devs who came up with it just refuse any suggestions to have expressive exceptions (that's what exceptions messages are for). It's impossible to actually know what could go wrong when calling any method.
- hombre_fatal 7y agoChecked exceptions deserve all the hate they get. It's not a good solution. Here's a random rant online that ends up pitching the more common solution of unchecked exceptions + exception rewrapping: https://phauer.com/2015/checked-exceptions-are-evil/ https://phauer.com/2015/checked-exceptions-are-evil/. And of course, in Rust, the rewrapping is basically forced upon you making for a really nice error-handling ecosystem.
- interlocutor 7y agoUnchecked exceptions are a mistake. If the method you call is modified to throw a new recoverable exception how will you be alerted to the issue? You won't. Your app will crash. With checked exceptions the compiler lets you know. That's much better.
- hello_moto 7y ago> I never understood the hatred checked exceptions received especially from the younger crowd. I'd say not "younger crowd" but rather developers who have never dealt with a workplace or situation where he/she is demanded to take error-handling (and recovery case) seriously. Different workplaces have different accountability when it comes to code-quality (including error handling and i18n). Java is more prevalent at some time in the past and it has built an amazing libraries of good/best practices while Python/PHP/JavaScript/Ruby weren't exposed to that level of commercial engineering scale in the past so when the developers came from the latter group, they rebel a bit when it comes to exceptions. Keep in mind that Java was born to address commercial needs thus there is more accountability compare to OSS languages. Just different mindset, different goals hence leading to different design decision.