[HN Gopher] Unchecked Java: Say goodbye to checked exceptions
       ___________________________________________________________________
        
       Unchecked Java: Say goodbye to checked exceptions
        
       Author : rogerkeays
       Score  : 74 points
       Date   : 2023-07-13 13:25 UTC (9 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | invalidname wrote:
       | FYI this too is already supported by Manifold...
        
         | jsight wrote:
         | That is referenced in the readme. :) TBH, I hadn't seen either
         | one before today, and they both look really nice.
         | 
         | The type-safe JSON feature in Manifold could be incredibly
         | useful.
        
           | invalidname wrote:
           | Yes. It's fantastic see:
           | https://debugagent.com/series/manifold
        
         | dgellow wrote:
         | What is manifold in this context?
        
           | invalidname wrote:
           | See my reply to jsight...
        
       | darklycan51 wrote:
       | Certified Try catch moment
        
       | nepthar wrote:
       | Reading through the comments, I realize this may be a minority
       | view - but I like coding for the happy path and letting
       | exceptional states crash. I find languages like go a bit harder
       | to parse quickly because I always have to "unwrap" the happy path
       | from all of the mixed in error handling.
       | 
       | I'm sure I'd get used to it eventually, but I like that unchecked
       | exceptions in Java are now an option!
        
         | raincole wrote:
         | > I like coding for the happy path and letting exceptional
         | states crash.
         | 
         | Of course everyone like coding like this. No one likes to code
         | error handling code.
         | 
         | Just like no one likes to code tests and documentations.
         | 
         | It turns out not every important thing is joyful.
        
         | the_gipsy wrote:
         | In practice there is a lot of paths that you don't want to
         | "crash" on, but try the next thing etc.
        
         | asddubs wrote:
         | I'm fine with doing this on purpose, but without a system like
         | checked exceptions, you do it without even really realizing
         | you're doing it. Checked exceptions point out the errors and
         | then let you decide whether it's something you should handle or
         | let it crash. It makes for more stable software.
        
           | code_runner wrote:
           | its only an illusion of stability. so many things can go
           | wrong outside of exceptions and all it does is add mandatory
           | lines of code (probably rethrowing as a runtime exception) to
           | every single consumer. It pollutes everything it touches.
        
         | ziml77 wrote:
         | If you actually let it crash that's fine, but a lot of people
         | just toss a catch (Exception ex) in there because they don't
         | know what exceptions will happen and don't want errors that
         | aren't actually a big deal to bring the system down. But the
         | issue with catching everything, even if you log before
         | continuing, is that the process may be in a bad state and
         | you're just continuing with it like that. I got to see a couple
         | database tables get absolutely mangled by a process that had an
         | exception that was caught and logged. That was a pain to clean
         | up since doing nothing more than restoring a backup would have
         | lost the entire day's worth of business operations in that
         | system.
        
       | edpichler wrote:
       | This dependency removes one of the best features of Java, which
       | make others think about what happens if things go wrong.
        
         | code_runner wrote:
         | only the things that someone acknowledges may go wrong
        
       | dtech wrote:
       | Most other languages agree that checked exceptions are not good
       | by not having them.
       | 
       | As for alternatives, Try/Result and similar monads have decent
       | adoption even in Java, but personally I quite like the Kotlin
       | philosophy [1] to not have generic error containers and either
       | use runtime exceptions for errors that cannot be reasonably
       | handled by the caller and encapsulate failures in the return type
       | if they can.
       | 
       | [1]
       | https://github.com/Kotlin/KEEP/blob/master/proposals/stdlib/...
        
       | clownvorld wrote:
       | The Trouble with Checked Exceptions A Conversation with Anders
       | Hejlsberg, Part II
       | 
       | https://www.artima.com/articles/the-trouble-with-checked-exc...
        
         | taftster wrote:
         | This is an insightful interview, thank you for the link. I'm
         | well read up on the topic, but this interview was still great
         | and is a good perspective on the checked/unchecked debate.
         | 
         | The fact that Java has introduced UncheckedIOException, in my
         | opinion, shows how _some_ people in the Java community have
         | come to believe that checked exceptions were a mistake
         | (understanding that lambda forced the issue). There 's probably
         | not too much to be easily done at this point, but consideration
         | for changing checked exceptions in the JDK to extend
         | RuntimeException sure would be interesting.
        
           | rogerkeays wrote:
           | I think this project shows how trivial it is to convert
           | checked exception errors to compiler warnings.
           | 
           | https://github.com/rogerkeays/unchecked/blob/f22c8cde3557de0.
           | ..
           | 
           | No need to change the type hierarchy. Just make it a compiler
           | option.
        
       | dejj wrote:
       | Lombok's @SneakyThrows
       | 
       | https://projectlombok.org/features/SneakyThrows
        
       | phoe-krk wrote:
       | _> Unchecked does not make any changes to your bytecode. This is
       | possible because the JVM does not know about checked exceptions.
       | It 's been the compiler holding you back all this time._
       | 
       | TIL!
        
       | asddubs wrote:
       | I actually quite like checked exceptions, and miss them in other
       | languages. My biggest gripe with exceptions is that a few calls
       | deep, you can no longer tell whether calling something might
       | throw or not, and what type of exception it might potentially
       | throw.
        
         | josephcsible wrote:
         | I like checked exceptions in principle too, but Java's
         | implementation of them is so bad that I hate them there. The
         | first major mistake is that it doesn't support exception
         | polymorphism (e.g., I wish you could pass a comparator to
         | Arrays.sort that throws checked exceptions, and have that call
         | to Arrays.sort itself then throw the same checked exceptions),
         | and the second is that the standard library makes a bunch of
         | exceptions checked that should be unchecked (e.g., IOException
         | from close()).
        
           | sam_lowry_ wrote:
           | Which is now commonly wrapped into UncheckedIOException.
        
         | _old_dude_ wrote:
         | Checked exceptions are a leaky concept in Java. The Java type
         | system has no union types so you can not have a generics method
         | that abstract more than one exception.
         | 
         | That's why Stream::map can not capture the checked exceptions
         | properly.
        
           | pharmakom wrote:
           | Yes! The problem with the Java implementation of checked
           | exceptions is the inheritance hierarchy, which encourages
           | catching the broadest possible error.
        
       | code_runner wrote:
       | To the credit of the die-hard Java community, you all really seem
       | to love and support even the most horrific syntax and awful
       | language features. You love the pain. The rest of us are here for
       | you... I promise software can be fun.
       | 
       | Checked exceptions are far and away the worst part of java and
       | I'm glad that no other language I've _personally_ encountered
       | have them!
       | 
       | On a more real note, I work at a shop with a fair amount of java
       | now and checked exceptions are definitely my biggest complaint. I
       | hope we adopt this, I'm sure we won't, but I'm glad to see a
       | little movement on what I feel is the crusty status quo in the
       | java world.
        
       | rho4 wrote:
       | Still hoping that Java will support such a choice (compiler
       | option) out of the box in the future.
       | 
       | Also, without IDE support such a plugin will never gain a lot of
       | traction.
        
         | zeedude wrote:
         | Manifold provides this feature and supports IntelliJ IDEA and
         | Android Studio.
         | 
         | https://github.com/manifold-systems/manifold/tree/master/man...
        
           | rho4 wrote:
           | I am using Eclipse. But will definitely keep this in mind,
           | should I every switch to IntelliJ.
        
       | lucasyvas wrote:
       | This is a mistake - checked exceptions and functional equivalents
       | are the clear path forward.
        
       | breadwinner wrote:
       | One of the biggest flaws in C#, in my experience, is lack of
       | checked exceptions. As an example, I wrote some very good code,
       | carefully tested it, made it work flawlessly, then suddenly it
       | started crashing. What happened? Someone made a change in a
       | function I was calling, and it started throwing a new exception.
       | This would have caused a compile error in Java, not a crash.
       | 
       | More on checked vs unchecked exceptions here:
       | https://forum.dlang.org/thread/hxhjcchsulqejwxywfbn@forum.dl...
        
         | skissane wrote:
         | > Someone made a change in a function I was calling, and it
         | started throwing a new exception. This would have caused a
         | compile error in Java, not a crash.
         | 
         | Not necessarily. If it started throwing a new RuntimeException,
         | it wouldn't have. There are also sneaky ways to throw checked
         | exceptions without declaring them, for example using Lombok's
         | @SneakyThrows annotation
        
           | breadwinner wrote:
           | Certainly you can intentionally break it, but then that's on
           | you.
        
             | skissane wrote:
             | A person can also accidentally break it - by adding some
             | new code which contains a bug which causes an unchecked
             | exception or error to be thrown (e.g. NullPointerException,
             | ArrayIndexOutOfBoundsException, StackOverflowError, etc).
             | 
             | Checked exceptions do nothing to protect against those
             | kinds of mistakes, which in my personal experience are
             | vastly more common than whatever mistakes for which they
             | may provide some protection
        
         | oh_sigh wrote:
         | Your code might have been very good, but it wasn't future
         | proof. It sounds like your code would have been fine if you
         | locked down the libraries you were calling to a fixed version
         | or something.
        
         | __jem wrote:
         | On the other hand, in lots of application code, there's often
         | not much to do other than crash or abort the request, and this
         | can often be safer than trying to proceed along code paths that
         | are almost always under-tested. We've all seen Java code where
         | a junior has decided to swallow an error without much thought.
         | This obviously isn't true for every use case, but failing fast
         | is often a pretty good policy.
        
         | taeric wrote:
         | I mean, maybe? Fun degenerate cases to consider: Someone throws
         | a Environment.Exit(0) into a random library you are using,
         | instant pain. Someone throws an infinite loop into a library
         | you are using, similar instant pain.
         | 
         | There is no magic language trick that can prevent you from
         | having to rerun all tests for your software if you update a
         | dependency. Pretty much period.
         | 
         | (I say this as someone that isn't really opposed to checked
         | exceptions.)
        
         | nradov wrote:
         | The trouble with checked exceptions is that they prevent you
         | from easily extending classes or implementing interfaces that
         | you don't control. Your new class might need to throw a checked
         | exception not included in the method signature. So then you
         | have to resort to hacks like wrapping the new checked exception
         | inside a runtime exception.
        
         | h4x0rr wrote:
         | Checked exceptions just always felt annoying to work with. Imo
         | Rust's Result type is more versatile
        
         | jabiko wrote:
         | Lets be honest. The more likely thing is that either the
         | coworker would use an unchecked exception or that they would
         | change the callsite to:                 try {
         | theUpdatedFunction();       } catch (MyNewCheckedException e) {
         | logger.warn("Whoopsie doopsie", e)         throw new
         | SomeUncheckedException("Something failed, idk", e)       }
         | 
         | Which really is a zero sum game. The code still breaks the same
         | way, but the checked exception gets eventually wrapped in an
         | unchecked one. We still would have the situation that someone
         | changed the _behavior_ of the function in a way that is
         | incompatible with your usage.
         | 
         | That bug should have been caught by tests, code reviews and
         | good communication.
        
           | __jem wrote:
           | Or, worse, you're reviewing a large diff, see the logger
           | statement but don't notice that the junior whose code you're
           | reviewing forgot to rethrow. Now you've accidentally entered
           | uncharted territory where way more scarier things can happen
           | to your application than just crashing. Of course this
           | shouldn't happen, but it does.
        
       | flerchin wrote:
       | Neat. Reminds me of lombok in that it really only affects
       | compile-time and makes for cleaner code in a way that many (but
       | not all) developers would want.
        
         | jameslars wrote:
         | Ugh Lombok! Literally everything it does is replaced by any
         | competent IDE with auto-generated methods, with the added
         | benefit of not requiring special build handling steps because
         | the library can't play by the normal annotation processing
         | rules.
         | 
         | There was maybe a time Lombok made sense. It does not anymore.
         | Death to Lombok.
        
           | nabogh wrote:
           | Disagree. Just because the ide wrote a bunch of boilerplate
           | for me at some point doesn't mean I can know the boilerplate
           | is unchanged without reading a bunch of getters and setters.
           | The mental burden of tiny classes is so much nicer to read.
        
           | never_inline wrote:
           | That's usually one time concern. Much better than having
           | boilerplate lying around IMO.
        
       | alanfranz wrote:
       | I don't want to remove all checked exceptions in random code,
       | btw. Even though they're not common, it can be bad if they're
       | unhandled in some cases. How can I know beforehand?
       | 
       | I'd like something to soften some of them, probably in a
       | configurable way, in some builtin interfaces btw. E.g. many
       | IOExceptions should really be unchecked.
        
       | macpete42 wrote:
       | catch them at the source and return sealed classes (used like sum
       | types) instead
        
       | watwut wrote:
       | Nothing better then having to guess whether the method may throw
       | an exception or not.
        
         | dtech wrote:
         | You already have to guess. All method calls van throw Runtime
         | exceptions.
        
         | code_runner wrote:
         | nothing better than someone assuming a method can only throw a
         | specific type of exception or won't throw at all.
        
       | nnnnico wrote:
       | Allowing unchecked exceptions in languages without explicit error
       | handling or the return of error values is a mistake IMO! Makes it
       | impossible to call a function safely
        
         | RGBCube wrote:
         | Exactly, C++ exceptions are horrible, you never know what
         | throws what. Java made C++ exceptions better by making them
         | explicit, so you always know what throws. Then Kotlin came and
         | made everything a unusable mess (don't get me wrong, I love
         | Kotlin, just hate that it doesn't have explicit exceptions).
         | 
         | I honestly think the best error handling strategy is employed
         | by Zig, then Rust. they're very explicit while not getting in
         | the way, you always know what throws what and what doesn't.
         | 
         | Rust has a little issue though, which is that people can make
         | their functions return `Result<T, Box<dyn Error>>` which makes
         | handling the returned error difficult, Zig does not have any of
         | that.
        
           | menaerus wrote:
           | It is true that C++ exceptions are not checked and are rarely
           | part of the interface signature but C++ exceptions are not a
           | tool for general error handling in the context you're
           | comparing them against Java, Zig and Rust. Java uses
           | exceptions for control flow while C++ doesn't. Zig and Rust
           | don't have exceptions at all. What Zig and Rust have as error
           | handling mechanisms C++ has them too. C++ exceptions are of a
           | literal meaning - exceptional cases which you don't expect to
           | happen and you usually don't know how to recover from. In
           | this case propagating the exception up to the thread entry
           | point and doing whatever in that case (logging, terminating,
           | restarting, etc.) is vastly better than language forcing me
           | to check for each possible exception all the way through the
           | whole function callstack - it's useless.
        
             | AnimalMuppet wrote:
             | In what way are Java exceptions part of flow control and
             | C++ exceptions aren't?
        
           | SomeRndName11 wrote:
           | I think the way Go does error handling is the best
           | compromise.
        
             | vips7L wrote:
             | It's honestly the worst compromise. You can trivially
             | ignore any error in Go and they don't contain contextual
             | information about the call stack.
        
             | the_gipsy wrote:
             | It's a successful compromise, but probably not the best. It
             | has a huge amount of little issues that simply aren't there
             | with FP error handling.
        
             | ziml77 wrote:
             | It's one of the worst I've seen. It adds so much cognitive
             | overhead to reading the code and makes it more annoying to
             | write. Every function call that can fail is followed by 3
             | lines to check for and return the error. The actual logic
             | quickly gets lost in there.
             | 
             | If it wasn't for the error handling, I would probably use
             | Go for some of my projects. It compiles to a native, self-
             | contained binary and still has the convenience of a garbage
             | collector, and of course has plenty of libs available due
             | to popularity.
        
             | kaba0 wrote:
             | Go's solution is like people couldn't decide on if they
             | should turn left, or turn right to avoid the cliff, so to
             | make everyone happy they drove straight into it.
             | 
             | It is literally the compiler enforced shitty error handling
             | from C's errno that is not even a sum type.
        
             | [deleted]
        
           | ziml77 wrote:
           | As far as I know Zig errors can't carry any data along. It's
           | nice having more details available about the failure.
           | 
           | Take for example if you call to an OS API and it returns an
           | error that wasn't documented to be returned from that
           | function. You can't return that code up the callstack using
           | Zig's error mechanism. Instead there's functions such as
           | unexpectedErrno and unexpectedError. Those call the
           | appropriate method to get a string representation of the
           | error and call std.debug.print to display it, then they just
           | return error.Unexpected.
           | 
           | That means that the caller doesn't have any control over how
           | the error is displayed. Meanwhile in Rust, you can add an
           | UnexpectedError variant to your enum and let it carry an
           | error code with it. The caller can then display that error
           | however they want.
           | 
           | I don't hate Zig's error handling by any means, but
           | personally I think Rust does it better. I'm happy to see that
           | C++ seems to be going with the Rust way by adding a Result-
           | like type std::expected.
        
             | RGBCube wrote:
             | Both have their issues, but I think the perfect error
             | handling mechanism would be something like the Zig way with
             | additional data.
        
         | nnnnico wrote:
         | (To clarify, i'm against exception based error handling, but
         | removing checked exceptions in languages without other
         | mechanisms of explicit error handling makes things more
         | brittle. For example in kotlin, where some Java practices to
         | throw all over the place are still in use, unchecked exceptions
         | makes things worse. You wont know if a function will throw
         | unless you inspect the code or trust it's documentation)
        
         | ars wrote:
         | Java just needs one small change to fix exceptions.
         | 
         | Instead of the exception _type_ being checked or unchecked, the
         | _throw_ should specify checked or unchecked.
         | 
         | So for example the first time the exception happens it can
         | throw a checked exception for the caller to deal with.
         | 
         | If the caller doesn't deal with it it can simply throw its way
         | all the way to the top without every single function needing to
         | declare they handle it.
        
           | kaba0 wrote:
           | The proper solution is effectful type systems that are
           | polymorphic to effects, like exceptions.
        
         | tasubotadas wrote:
         | The entire community disagrees with you[1]
         | 
         | >Makes it impossible to call a function safely ??
         | catch(Exception e) anyone?
         | 
         | [1] https://literatejava.com/exceptions/checked-exceptions-
         | javas...
        
           | ivanche wrote:
           | A lot of people, probably. But definitely not the entire
           | community [0].
           | 
           | [0] https://www.yegor256.com/2015/07/28/checked-vs-unchecked-
           | exc...
        
         | _old_dude_ wrote:
         | In Java, no method call is safe (Haskell/Rust safe), the
         | langage will hapilly throws a null pointer exception or an out
         | of memory error.
         | 
         | But I don't think C#, Kotlin or Scala are less safe than Java
         | even if they do not have the concept of checked exceptions.
        
           | rogerkeays wrote:
           | Good point...
        
           | kaba0 wrote:
           | Haskell and Rust can also throw exception/panic respectively
           | in any call, ever.
        
             | _old_dude_ wrote:
             | Technically true, but Haskell forces you to use the IO
             | monad and you can not really recover from a panic in Rust,
             | the thread is dead.
        
               | kaba0 wrote:
               | head []
               | 
               | No monad, nothing, just an exception.
        
         | rho4 wrote:
         | I would argue the opposite: Junior and senior developers alike
         | catch and swallow or log checked exceptions all over the place,
         | making it impossible to know if your method call was successful
         | or not.
        
           | Supermancho wrote:
           | At some point you want to swallow all checked exceptions at
           | some point, because letting the application leak stack traces
           | to the client is generally a bad idea.
        
           | misja111 wrote:
           | This is why you should have code reviews. Letting juniors
           | submit code without reviewing it is a recipe for disaster, no
           | matter which language you are programming in. And if it's a
           | senior, well someone should have a word with him ..
        
       | _ZeD_ wrote:
       | please, for the love of what's good in the world... NO. checked
       | exceptions ARE A FEATURE, a CORE ONE.
        
         | lesuorac wrote:
         | There's a reason using streams with checked exceptions is so
         | painful; checked exceptions are going the way of the dodo.
        
         | dtech wrote:
         | They are so bad even Java stdlib stopped using them for the
         | most part. Nearly all Java 8+ APIs like streams use unchecked
         | exceptions.
        
       | bedobi wrote:
       | Exception based error handling is so bad and unsafe that adopting
       | functional error handling with Either, Try etc as implemented by
       | functional addon libraries for many languages, while not yet
       | common, in time it will become the new default even in OO
       | languages. (just like it's been the default in functional
       | languages for decades)
       | 
       | Functional error handling types are much simpler, safer and more
       | powerful.
       | 
       | Simpler because they don't rely on dedicated syntax- they're just
       | regular objects no different to any other object.
       | 
       | Safer because unlike exceptions, they force callers to handle all
       | potential outcomes, but no more. (no risk of ignoring errors and
       | no risk of catching a higher level of error than desired,
       | ubiquitous bugs in exception based error handling)
       | 
       | Powerful because they support map, flatmap, applicative etc,
       | making it easy to eg chain multiple computations together in
       | desired ways, which is unwieldy and bug prone when using
       | exceptions.
       | 
       | > What is wrong about dedicated syntax
       | 
       | It adds complexity to the language! It could be that, when
       | learning Java, Kotlin and any other language, we learn that
       | methods return what they say they do... and that's that. No weird
       | dedicated syntax and magic, special treatment for returning
       | anything other than the happy path, and the HUGE complexity that
       | comes with it, eg the dedicated syntax itself and how it behaves,
       | differences between checked and unchecked exceptions, hierarchies
       | of exceptions etc etc.
       | 
       | > Exceptions are easier
       | 
       | But that's the point, they're not.
       | 
       | Exceptions based error handling is unnecessary, hugely complex,
       | doesn't compose at all, obfuscates or straight up hides what can
       | go wrong with any given call, so leads to countless trivially
       | preventable bugs... I could go on. And after decades of use,
       | there's still no consensus about what exceptions should be or how
       | they should be used. Exceptions are a failed experiment and I
       | have no doubt that in ten years, Java, Kotlin and many other
       | languages will acknowledge as much and move away from it the same
       | way Joda Time outcompeted and replaced the horrible Java date and
       | time library.
        
         | misja111 wrote:
         | Exception based error handling is unsafe when they are
         | unchecked exceptions. Checked exceptions however are as safe as
         | Either, Try, Monads, Applicatives or whatever. You are forced
         | to declare them in your method signature, the caller is forced
         | to either handle them or rethrow them + declare them as well.
         | And I guess this is precisely why so many developers hate them;
         | they don't like the extra work they have to do to catch all
         | those edge conditions. This is why we see so many empty catch
         | blocks or upcasting to Exception or even Throwable; it is
         | laziness.
         | 
         | I would also argue that checked exceptions are no more complex
         | than Eithers, Try or Applicatives. Actually passing Eithers or
         | Applicatives around everywhere can easily clutter your code as
         | well, IMO it can be worse than checked Exceptions.
        
           | renewiltord wrote:
           | Ultimately, core language features that make things ergonomic
           | enough for lazy developers to get it right will result in
           | better software.
           | 
           | And ones that are so unergonomic that only industrious
           | developers will get them right will result in worse software.
           | 
           | Modern languages allow these to be zero cost abstractions so
           | there is little tradeoff.
        
             | misja111 wrote:
             | I have been coding in Scala/ Haskell for the last 10 years,
             | before that 15 years in Java. What I'm seeing is that there
             | are as many lazy developers in FP as there are in Java,
             | maybe even more. And despite all the nice safeguards that
             | FP provides, there are still plenty of ways for lazy devs
             | to work around them.
             | 
             | For instance the IO monad, which is used everywhere
             | Scala/Cats. It can contain a result or an error. If you
             | don't feel like checking for an error after you called some
             | method, you can just pass it up and return the IO from your
             | method. Does that sound familiar? It behaves just like a
             | checked exception, the only difference is that methods
             | don't need to declare any error or exception in their IO
             | signature.
        
           | skissane wrote:
           | > And I guess this is precisely why so many developers hate
           | them; they don't like the extra work they have to do to catch
           | all those edge conditions
           | 
           | I hate checked exceptions when they force me to handle an
           | exception which I know is impossible given the arguments
           | passed to the method. For example, some older Java APIs take
           | an encoding name and force you to handle the checked
           | UnsupportedEncodingException - even when the encoding name
           | was something like "US-ASCII" or "UTF-8" which is required to
           | exist by the Java standard, and if somehow they didn't there
           | is often no possible error recovery than just crashing. This
           | has been fixed in newer versions by introducing new APIs
           | which throw an unchecked exception instead, and also by
           | introducing constant objects for the standard charsets
        
           | bedobi wrote:
           | > Checked exceptions however are as safe
           | 
           | No. If I add a checked UserNotFound exception to a getUser db
           | call, you can bet someone higher up the stack will do try
           | catch Exception e, so now they're catching OutOfMemory and
           | who knows what else.
        
             | kaba0 wrote:
             | As opposed to force unwrapping a Result type? Also,
             | OutOfMemory is an error, exceptions won't catch it.
        
               | DarkNova6 wrote:
               | What about DivisionByZero, NumberFormatexception,
               | ArrayStoreException?
               | 
               | There are countless examples of rare but legit non-
               | obscure use-cases. And even if your code is fine, you
               | can't expect the same for the libraries you are using.
               | 
               | And some exceptions make frequent non-happy paths more
               | visible. Most of all IOException, because IO can _always_
               | fail for all the wrong reasons (because the failing of
               | this exception is outside of the JVM's influence, it is
               | rightfully a checked exception). And often you simply
               | don't want to do the error handling at call-site but
               | propagate to the code which is controlling the use-case.
        
               | kaba0 wrote:
               | Was that comment meant for me?
        
             | vips7L wrote:
             | OutOfMemoryError isn't an exception so they would not be
             | catching it.
        
               | bedobi wrote:
               | whatever, the point is exception allows and encourages
               | people to catch less and more than they should
        
               | vips7L wrote:
               | Sometimes top level code needs to do this. It's not
               | always wrong. It's not always black and white in
               | programming.
        
               | bedobi wrote:
               | > Sometimes top level code needs to do this
               | 
               | come on, it's not as if anyone disagrees with that, but
               | that's extremely, extremely rare, overwhelmingly, callers
               | are just dealing things like with UserNotFoundException,
               | NullPointerException, what have you, and there's no
               | reason why the compiler should happily let you catch
               | Exception (or nothing) when it could just be giving you
               | an honest object back
        
               | wewtyflakes wrote:
               | It has not been extremely/overwhelmingly rare in my
               | experience. Further still, within the context of Java,
               | exceptions are objects.
        
             | jbverschoor wrote:
             | Then that person should get a Java 101 class. Or is this
             | the current "staff software engineer" level of skills?
        
               | bedobi wrote:
               | This is the most common reply I see whenever anyone
               | proposes a safer, better way of coding, and it's not a
               | good one. "Just get better" like oh ok except that in the
               | real world people are gonna people and even the best
               | programmers in the world make mistakes and do dumb, lazy
               | shit. Our tools should be designed such that the safest,
               | most correct way to do anything has the path of least the
               | resistance. Not happily allow you to ignore exceptions,
               | or catch less or more exceptions than required.
               | Functional error handling corrects this, exceptions do
               | not. Anyway, it's clear we're not going to agree, and you
               | win, since so far the industry is still stubbornly
               | clinging to exceptions, despite them being a failed
               | feature in every language they're in.
        
             | twelve40 wrote:
             | > you can bet someone higher up the stack will do try catch
             | Exception e
             | 
             | But that's laziness on the caller's part. If I offer a
             | method but the caller decides to do reckless lazy crap with
             | it, there are many different ways to get there in any
             | language. I typically call those out (Exception e) at code
             | reviews.
        
               | bedobi wrote:
               | This is the most common reply I see whenever anyone
               | proposes a safer, better way of coding, and it's not a
               | good one
        
               | esafak wrote:
               | ...because doing the right thing should be convenient,
               | for its own good.
        
         | kaba0 wrote:
         | Checked exceptions are exactly analogous of Result/Either
         | types. They are just built into the language with syntactic
         | sugar, automatically unwrap by default (the most common
         | operation), can be handled on as narrow or wide scope as needed
         | (try-catch blocks), does the correct thing by default (bubbling
         | up), and _stores stack traces_!
         | 
         | In my book, if anything, they are much much better!
         | Unfortunately they don't have a flawless implementation, but
         | hopefully languages with first-class effects will change that.
        
           | titzer wrote:
           | > Checked exceptions are exactly analogous of Result/Either
           | types.
           | 
           | No, they aren't.
           | 
           | They are not compositional. You may want to write `f(g())`
           | but there's no way to write the parameter type of `f` to make
           | this work (in Java). That's because checked exceptions are an
           | "effect" that would require extending the Java type system.
        
             | kaba0 wrote:
             | Read my last paragraph.
        
               | the_gipsy wrote:
               | So they are better... in a theoretical implementation.
        
           | [deleted]
        
         | The_Colonel wrote:
         | > Safer because unlike exceptions, they force callers to handle
         | all potential outcomes, but no more.
         | 
         | Which is bad. In 99% of cases I have no specific error handling
         | for the given problem. Just let the process crash or be handled
         | by application server / framework.
         | 
         | Representing these kinds of error conditions in the code which
         | I have no interest in handling is just noise.
        
           | sam_lowry_ wrote:
           | Especially because Kubernetes and the like work as if the
           | application must die anyway. Pretty much like PHP CGI
           | invocations always worked since 1998 or so.
        
           | bedobi wrote:
           | The problem with that is you lose all ability to understand
           | or control what a given endpoint will return in any given
           | situation
           | 
           | > I have no specific error handling for the given problem
           | 
           | then pass it on and decide what to do with it at the system
           | boundary
           | 
           | in the db:
           | 
           | fun getUser(uuid: UUID) : Either<Error, User>
           | 
           | middle layers: pass around the either, you can map, flatmap
           | etc on it to chain it with other computations there
           | 
           | then in the resource layer
           | 
           | return user .fold({ error -> when(error) { is DbError ->
           | HttpResponse.InternalServerError() is UserNotFoundError ->
           | HttpResponse.NotFound } }, { user -> HttpResponse.ok(user) })
           | 
           | Then, at every layer, each method explicitly says in the
           | method signature what it returns. There's no need to look
           | around each and every line of every method in every layer, or
           | in the framework, or some global exception handler, to figure
           | out what will happen. Developers can tell from a glance at
           | the resource method what the endpoint will return for each
           | outcome, in context.
           | 
           | Things like being unable to communicate with the DB or not
           | finding a user are not exceptional, they're entirely
           | expectable when you're calling a db to look for a user and
           | should be modeled accordingly.
        
             | The_Colonel wrote:
             | > The problem with that is you lose all ability to
             | understand or control what a given endpoint will return in
             | any given situation
             | 
             | Why? In the simplest (but pretty common) case it's:
             | 
             | - successful response
             | 
             | - generic error message
             | 
             | > then pass it on and decide what to do with it at the
             | system boundary
             | 
             | Yes, but if in 99% of cases I only pass it on, it's just
             | visual noise. Noise you become blind to, and it loses
             | meaning.
             | 
             | > Developers can tell from a glance at the resource method
             | what the endpoint will return for each outcome, in context.
             | 
             | They can't really because you end up with some very generic
             | error type anyway. Any typical service can have IOError,
             | DBConnectionError, SQLError, OutOfMemoryError,
             | StackOverflowError plus many other application specific
             | errors. You end up with bulk of your methods returning
             | Either<Error, Something> which is then meaningless.
        
               | bedobi wrote:
               | You're describing the problems with exception, not using
               | Either... When using Either, you never fold on those
               | errors individually... Anyway, it's clear we're not going
               | to agree, and you win, since so far the industry is still
               | stubbornly clinging to exceptions, despite them being a
               | failed feature in every language they're in.
        
               | WirelessGigabit wrote:
               | It feels like you're trying to use Exceptions as a way to
               | steer your logic, otherwise why would you need to know
               | why an operation failed to such detail?
               | 
               | Your controller method cannot act differently on a
               | DBConnectionerror or OutOfMemory error.
               | 
               | Not to mention that exceptions cause developers to use
               | them as control flow mechanisms.
               | 
               | For example searching a user by id. If the database
               | returns 0 records, is that reason to throw an exception?
               | Well, doing result[0] results in IndexOutOfBounds due to
               | result being [].
               | 
               | But the reality is that the user not being there isn't
               | exceptional. Typos are common. By using Result<T, E> or
               | Either you enforce the developers to think more about
               | their flow. One can write the method like this:
               | fn find_by_id(id: usize) ->Result<UserResult, Error> -> {
               | let raw_db_result = db.search("SELECT id, first_name,
               | last_name FROM user WHERE id = ?", id)?;
               | match raw_db_result {                None =>
               | Ok(UserResult::NotFound),                Some(r) => {
               | let only_user = r.ensureOneResult()?;
               | let user = mapDBUserToUser(only_user);
               | Ok(UserResult::User(user))                }            }
               | }
               | 
               | What about Error? Reality is that I don't really care.
               | Error doesn't contain anything that is actionable. Either
               | the whole chain succeeds and returns a valid result or
               | the Error. The caller wants to find a user by id. There
               | is one, or there isn't. All the rest is just errors that
               | they'll pass on too. And in the end they get logged and
               | result into Error 500.
               | 
               | A 404 is actually a valid result.
               | 
               | Now, if I were to use a throw new UserNotFoundException()
               | for no user found you end up with generic try catches
               | catching too much. And now someone needs to go and take
               | it all apart to identify that single Exception that they
               | want to deal with separately.
               | 
               | Whereas if I want to add a state in my enum the callers
               | _MUST_ update their code due to how Rust works.
        
               | smallerfish wrote:
               | Or Kotlin's approach:                  fun
               | getUser(userId: UUID): User?
               | 
               | You cannot treat this result value as a `User` in code
               | that calls this; though, once you null check it in your
               | service layer, you can pass it on as `User` in the not-
               | null branch. Null is nothing to be afraid of if the
               | language forces you to declare nullability and deal with
               | it.
        
               | andrekandre wrote:
               | this is a very common in obj-c too
               | 
               | on the other hand, depending on what your trying to do
               | you might want to provide more context about what
               | happened to the user/programmer
               | 
               | in swift you can change a throwing function to a nullable
               | with `try?` so even if `getUser()` throws, you can keep
               | it simple if thats what is appropriate
               | guard let user = try? getUser(id: someUUID) else {
               | return "user not found"       }
               | 
               | as an aside, swift "throws" exceptions but these are just
               | sugar for returning basically an Result<T,E> and compose
               | much better than traditional stack unwinding exceptions
               | imo
        
               | The_Colonel wrote:
               | > otherwise why would you need to know why an operation
               | failed to such detail?
               | 
               | I'm not defining the errors like DBConnectionError or
               | OutOfMemoryError - it's the framework/platform which
               | defines them and throws/returns them.
               | 
               | > But the reality is that the user not being there isn't
               | exceptional.
               | 
               | That depends. In some contexts it is not exceptional
               | (getting user by ID given as an argument to webservice),
               | in that case using Maybe type is great. In other contexts
               | it is very much exceptional (e.g. signed JWT refers to a
               | non-existing user) and throwing Exception makes more
               | sense.
               | 
               | > What about Error? Reality is that I don't really care.
               | Error doesn't contain anything that is actionable. Either
               | the whole chain succeeds and returns a valid result or
               | the Error. The caller wants to find a user by id. There
               | is one, or there isn't. All the rest is just errors that
               | they'll pass on too. And in the end they get logged and
               | result into Error 500.
               | 
               | Which is the same as exception. But now you have this
               | visual noise of something you claim you don't care about.
               | An unchecked exception makes this irrelevant noise go
               | away.
               | 
               | > Now, if I were to use a throw new
               | UserNotFoundException() for no user found you end up with
               | generic try catches catching too much. And now someone
               | needs to go and take it all apart to identify that single
               | Exception that they want to deal with separately.
               | try {             ...         }         catch
               | (UserNotFoundException e) {             // handle ...
               | }
               | 
               | I'm catching exactly the exception I want. Where I'm
               | catching too much? Where do I need to take it apart?
               | 
               | (This particular example of exception usage is bad,
               | though, as it smells of exception control flow. Here
               | using Maybe type would be better)
               | 
               | > Whereas if I want to add a state in my enum the callers
               | _MUST_ update their code due to how Rust works.
               | 
               | Which is good for some cases where the enum describes
               | "business" cases.
               | 
               | But it is pretty bad for truly exceptional cases (which
               | are unlikely to be handled anyway). Library adding a new
               | exceptional case will break its clients, which seems like
               | a bad trade-off (again, for truly exceptional cases).
        
               | bedobi wrote:
               | WirelessGigabit gets it. That last fact is huge -
               | functional error handling forces callers to handle (or
               | pass on) exactly what can go wrong, no more, no less.
               | (unlike exceptions)
        
           | titzer wrote:
           | Situations vary a lot. In general there's no guarantee that a
           | far outer scope is going to know how to deal with errors from
           | deep inside some nested calls except for some very general
           | logic, like failing a whole operation.
        
             | kaba0 wrote:
             | Even more commonly, there is no meaningful error handling
             | to be done at a layer above the call.
        
         | spion wrote:
         | Exceptions are not unsafe.
         | 
         | That said, not modelling them in the type system is a mistake.
         | But the model has to be useful - knowing what functions can or
         | cannot throw is useful, knowing what they throw, less so.
         | 
         | (They are safe because of try-finally / try-with-resources)
        
           | bedobi wrote:
           | They are unsafe because they invariably result in people
           | either ignoring them or catching more than they should, or
           | less than they should, and the compiler happily lets you do
           | that, EVEN when you're using checked exceptions.
        
             | AnimalMuppet wrote:
             | How will the compiler let you catch less than you should
             | when using checked exceptions?
        
         | asddubs wrote:
         | You mean something like C++ Optional, right? Those are nice, if
         | the language supports generics.
        
       | zeedude wrote:
       | Manifold already provides this feature:
       | 
       | https://github.com/manifold-systems/manifold/tree/master/man...
       | 
       | With an article having the same name as your post:
       | 
       | https://github.com/manifold-systems/manifold/blob/master/doc...
        
       | galaxyLogic wrote:
       | This seems like a major improvement for Java readability as shown
       | by the examples.
       | 
       | What I don't like is the examples of having to use Maven or
       | Gradle to install it. Why does that have to be so verbose, and
       | something written in another language?
        
         | rogerkeays wrote:
         | Ha, yeh. At first I had the command line examples first, but
         | everyone was going on and on about maven and stuff, so I
         | figured that's what the audience wants.
         | 
         | [EDIT] I moved the command line stuff back to the top of the
         | README. XML makes my eyes bleed too...
        
         | [deleted]
        
       | edpichler wrote:
       | Later you detect an error, more expensive it is to fix it.
        
         | ars wrote:
         | That's not really true.
         | 
         | Vast majority of the time either the caller will fix it, or it
         | simply does not need to be fixed at all it's simply passed all
         | the way up to the top.
        
       ___________________________________________________________________
       (page generated 2023-07-13 23:01 UTC)