5 ms·
Java does this with checked exceptions and it’s a huge hassle to work with.
by enticingturtle 4y ago
Java does this with checked exceptions and it’s a huge hassle to work with.
- tsimionescu 4y agoTo be fair, there are two reasons why it's considered a hassle. One reason, which is a bad reason, is that many people just don't like to document and handle errors. Instead of being happy that the compiler is telling them that they forgot to handle or declare an IOException from this call, they get annoyed that it's yelling at them "for no reason". This is simply lack of understanding/care for how you program. The other reason, which is actually a problem with the language that could be fixed, is that Java doesn't allow functions to be polymorphic in their Exception types, like it does for argument and return types. This makes higher-order constructs very annoying - for example, `stream.map(Function)` should `throw` the same Exceptions that `function` throws, as should `Arrays.sort(array, Comparator)`. Without this capability, you end up with an extremely ugly and brittle pattern of doing: //V foo(T x) throws SomeException(); Stream<V> bar(Stream<T> stream) throws SomeException { try { return stream.map( (x) -> { try { return foo(x); } catch (SomeException e) { throw new RuntimeException(e); }); } catch (RuntimeException e) { if (e.getCause() instanceof SomeException) { throw (SomeException)e.cause(); } else { throw e; } } } When all you wanted was: Stream<V> bar(Stream<T> stream) throws SomeException { return stream.map(Foos::foo); } This huge verbosity is obviously unnecessary and ugly, and could be removed with some compiler support (compiler could just insert this), or some more complex runtime support. In my opinion, if Java did this, it would actually have the best error handling mechanism of any language on the market - much better than Haskell or Rust.
- Tainnor 4y ago> One reason, which is a bad reason, is that many people just don't like to document and handle errors. Instead of being happy that the compiler is telling them that they forgot to handle or declare an IOException from this call, they get annoyed that it's yelling at them "for no reason". This is simply lack of understanding/care for how you program. Usually, what happens is that you call some library code (or some code that a teammate wrote) and that code will declare an IOException or something like that. In many cases, there's no point in handling that, as that file that you're trying to open or similar isn't supplied by the user but e.g. a static resource. That's the entire point of the article, that panicking when encountering a violated precondition is totally acceptable. The unchecked vs. checked exception distinction also suffers from the fact that it's usually the call site, and not the declaration site, that knows whether an exception is recoverable or not. Java checked exceptions would be fine if it had something like "unwrap", which just converts a checked exception to an unchecked one, but it doesn't, and that's why everyone kind of hates them.
- tsimionescu 4y ago> Java checked exceptions would be fine if it had something like "unwrap", which just converts a checked exception to an unchecked one, but it doesn't, and that's why everyone kind of hates them. It's a bit verbose, but try{ doThing();} catch (Exception e) {throw new RuntimeException(e) ;} is exactly that, and in today's Java it is very easy to put in a utility function. > The unchecked vs. checked exception distinction also suffers from the fact that it's usually the call site, and not the declaration site, that knows whether an exception is recoverable or not. But that is true of every possible compiler-enforced error handling mechanism. The declaration site is responsible for declaring what errors it can produce (through exceptions, result types, multiple returns, error codes, the Either monad etc.), and the compiler ensures that every call site handles those error values in some way.
- Tainnor 4y ago> It's a bit verbose, but try{ doThing();} catch (Exception e) {throw new RuntimeException(e) ;} is exactly that [...] The verbosity is exactly what is bothering most people. It's annoying to write and clutters the code. Even if you yourself agree that it should be written like that, your team mates will maybe not. The "swallow an error and pretend it didn't happen" pattern is incredibly common in Java from my experience and it simply wouldn't be so common if there existed something like "unwrap()". > and in today's Java it is very easy to put in a utility function. I had to do quite a bit of trial and error and googling to see how this can be done (you have to use the "Callable" interface, instead of something like "Supplier"). I suspect, this will be the same for most people, who wouldn't know how to write this, or just wouldn't bother. And even if you add this, you'd have to call it from some utility class, which makes the call site much more verbose. Defaults matter, and if there is no good built-in solution for this in Java, people will not use it. > But that is true of every possible compiler-enforced error handling mechanism. Not every language bothers with a compiler-enforced handling mechanism, precisely because it's hard for the compiler to predict whether an error should be recovered from or not, or at which part of the stack it should be handled. If you do want compiler-enforced error handling, result types are probably better because they are regular language constructs that can be manipulated and passed around in regular ways, and it's easy to convert them to a panic, as with Rust's "unwrap()". This is also why Kotlin switched away from checked exceptions and now maintains that if you do care about compiler-enforced error handling, you should use Result types instead. Now, that said, I wouldn't mind better tooling in languages with exceptions (Java and Kotlin, for example), where I could ask the compiler about a function and it could tell me about all exceptions that could (transitively) be thrown from that function, or an annotation to the effect of "please, compiler, verify that this function only throws these types of exceptions (be they checked or unchecked)". But that's something to be used judiciously for some critical code paths, and not everywhere necessarily.
- int_19h 4y agoOne of the early lambda proposals for Java - the one by Neal Gafter, if I remember correctly - actually had exception parameters for generics (including type unions), so you could do HOFs like that. But, at the end of the day, they went for something simpler.
- girvo 4y agoIt doesn’t have to be. Nim’s checked exception-like “raises” pragma is quite lovely to work with.