Retiring Checked Exceptions Was: Throwing Functions

Nathan Reynolds numeralnathan at gmail.com
Sat Nov 12 22:56:04 UTC 2022


> I've found that there are a lot of people out there who quietly think
checked exceptions are ... pretty OK.

Count me in this camp.  Checked exceptions are so much better than error
return codes.

Changing everything to unchecked exceptions would be terrible.  Step 1 of
programming is to write code that works for the main case.  Checked
exceptions ensure that some of the corner cases must be dealt with, and if
handled well, the code will be more robust.

For example, I wrote some code that tried to delete from the wrong part of
the file system.  Because I handled the IOException, the code was able to
continue in the right part of the file system and the functionality wasn't
broken.  I can take care of the bug later instead of having to do so right
away.

On Sat, Nov 12, 2022 at 3:50 PM Brian Goetz <brian.goetz at oracle.com> wrote:

> I appreciate John's points here.  I don't necessarily agree with all of
> them (there's a spectrum of opinions, and that's fine, though I find #3
> especially compelling), but I think this illustrates something about the
> exceptions debate that is easy to miss: the people who hate checked
> exceptions are much noisier about it than those who don't, and so they are
> likely to be fooled by confirmation bias since all the other loud voices
> seem to agree with them.  But I've found that there are a lot of people out
> there who quietly think checked exceptions are ... pretty OK.  Maybe even a
> majority.  They just don't want to engage in a big debate, they'd rather
> get their work done.
>
>
>
>
> On 11/12/2022 11:16 AM, John Hendrikx wrote:
>
> I very much disagree with this position, so I will make a case for checked
> exceptions:
>
> 1) Without checked exceptions, using exceptions for alternative outcomes
> as part of the contract of an API would no longer be possible. This is
> because adding a new exception to the API would no longer be a breaking
> change. Documentation is not going to save you here.  Effectively,
> exceptions would have only two uses left (like in other languages): fatal
> JVM breaking errors and to signal mistakes that could have been prevented
> by the programmer.  Using them to signal unpreventable errors would be
> highly suspect and likely quickly be considered a bad practice.
>
> In other languages (but also in some Java API's that use runtime exception
> to signal unpreventable errors) the compiler cannot help you with these
> alternative outcomes as it isn't even required to specify them as part of
> your API. It helps you with the return value, but not for any other
> outcomes. These other outcomes will now have to guessed, gleaned from the
> documentation (if it is up to date), or by doing manual code mining (of the
> called function and all the functions it calls).  Worse, these outcomes can
> change in later releases without any warning as they're non-breaking
> changes.
>
> 2) API's without checked exceptions will necessarily become more
> cluttered than their counterparts with checked exceptions. They would have
> to return wrappers or unions or special values to enforce proper use. API
> design like this prevents easily deferring the handling of rare occurrences
> higher up the call chain with standard idioms to keep the current code path
> clean and to the point.
>
> 3) Removing checked exceptions now would break the **design** of many
> current API's, making them dangerous to use without the checks and balances
> that were present when they were designed.
>
> 4) Earlier design decisions to exclude checked exceptions from new idioms
> and API's were a mistake that should be rectified not doubled down upon. A
> large part of the momentum against checked exceptions comes from the clumsy
> way that checked exceptions must be handled while using these new features
> that were essentially incompatible with existing Java.  Even with the
> current status quo, it is possible to design API's that do allow checked
> exceptions in combination with functional programming, it is just
> cumbersome to design these API's without a dedicated union type (for
> exceptions).  They either support only one checked exception or some
> limited number.  Their use is however as transparent as API's that do not
> support checked exceptions.
>
> 5) The fact that the value of checked exceptions is hard to recognize or
> cumbersome for beginners or non-Java developers should not have any bearing
> on language design.
>
> Checked exceptions differentiate between the common preventable
> programming errors and essential business logic. The difference between a
> runtime exception (leaving out Error) and a checked exception is that one
> can only occur in incorrect code, while the other can occur even in correct
> code. One indicates a preventable programming mistake and the other a
> missed path in your logic.  Throwing these on the same heap will
> effectively reduce the usefulness of all exceptions to programming mistakes
> (and fatal errors) only.
>
> 6) If anything, I think there should be way to promote runtime exceptions
> to checked exceptions within certain modules.  This would allow API /
> library designers the option to have the compiler check their code for
> missed documentation and missing logic even for what externally would be a
> runtime exception.  This would be especially useful during initial design
> and also later refactoring as the incredibly useful compiler checks for
> checked exceptions could be extended to proper use and propagation of
> runtime exceptions within that module.
>
> --John
>
>
> ------ Original Message ------
> From "Remi Forax" <forax at univ-mlv.fr>
> To "Nathan Reynolds" <numeralnathan at gmail.com>
> Cc "amber-dev" <amber-dev at openjdk.org>; "amber-spec-experts" <
> amber-spec-experts at openjdk.java.net>; "Ron Pressler" <
> ron.pressler at oracle.com>
> Date 12/11/2022 08:58:57
> Subject Retiring Checked Exceptions Was: Throwing Functions
>
> [promoted to amber-spec-experts]
>
> I think we should tackle this problem at its core and retire the notion of
> checked exceptions
>
> The written are on the wall since quite some time, but we have not yet
> acknowledge it.
>
> Let's recap the issue and the clues pointing in that direction.
>
> I believe the first straw can be tracked back to the inability of the type
> system to create a type variable containing the union of exceptions when
> generics where introduced in Java 5.
> It's the reason why there is no ThrowableConsumer, as an example, imagine
> that a stream can declare a checked exception Stream<T, E> and a user call
> map() that takes a ThrowableFunction,
> we need a way to express that the resulting stream is that combine the
> exceptions of the original stream and the exceptions that may be raised by
> calling the function of map.
>
>   Stream<T, E extends Exception>  -->  map(ThrowableFunction<T, R, F
> extends Exception>)  -->  Stream<R, E | F>
>
> Or perhaps from the beginning of Java, adding a checked exception to a
> "throws" colors the function the same way the keyword async or Rust
> lifetime type information color a function [1]. Adding a checked exception
> to a function is not a backward compatible change.
>
> So we have some of idioms that beginners need to internalize to try
> workaround checked exceptions,
> - The oldest is i think, Thread.currentThread().interrupt() which allows
> to silence an InterruptedException at the price of continuing to execute
> the code until the next blocking call
> - UncheckedIOException and IOError that both wrap an IOException into an
> unchecked exception allowing tunneling of checked exceptions ; wrapping an
> IOException and unbundle it later.
> - Unsafe.rethrow (this one was retired)
> - the unfamous rethrow using erasure to see a checked exception as an
> unchecked exception
>    static <T extends Throwable> AssertionError rethrow(Throwable cause)
> throws T {
>       throw (T) cause;
>    }
> - IntelliJ has changed the default try/catch snippet to instead of calling
> printStackTrace() to throw a RuntimeException wrapping the exception.
>   This simple change is i believe the best change to Java in the recent
> years (perhaps toes to toes with records), at least now the code of my
> students does not print the stack trace and resume its course when an
> exception occurs.
>     try {
>      ...
>     } catch(FooException e) {
>       throw new RuntimeException(e);
>     }
>
> Also no language presented as potential successor of Java, neither Scala
> nor Kotlin have checked exceptions, because functions with checked
> exceptions do not compose.
> If Java wants to be the next Java, we will have to drop checked exceptions
> at some point.
>
> I don't see the relevance of what other languages are doing with their own
> agenda's. There is no need to drop checked exceptions, they just need to be
> an integral part of the whole. Removing them doesn't make Java the next
> Java, it just makes it yet another language with slightly different syntax
> that succumbed to a vocal minority that values simplicity over safety.
>
> The good news is that seeing all exceptions as unchecked exceptions is a
> backward compatible change, "throws" can still be supported for
> documentation purpose, the compiler can emit a warning instead of an error
> if there is no catch corresponding to a checked exception and allow
> everyone to catch any exceptions in the code.
>
>
>
>
> I think we should recognize that the idea of checked exceptions was a good
> idea on paper but not a good one in practice and work to retire the concept
> of checked exceptions from Java.
>
> Rémi
>
> [1]
> https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/
>
> ------------------------------
>
> *From: *"Nathan Reynolds" <numeralnathan at gmail.com>
> <numeralnathan at gmail.com>
> *To: *"amber-dev" <amber-dev at openjdk.org> <amber-dev at openjdk.org>
> *Sent: *Saturday, November 12, 2022 5:47:10 AM
> *Subject: *Throwing Functions
>
> I am sorry if I am very late to the discussion.  Consumer, Function,
> Predicate, and Supplier don't allow for throwing checked exceptions.  This
> feature is needed in many cases.  What about adding a variant that allows
> for throwing checked exceptions?  For example, a ThrowableConsumer could be
> defined as such...
>
> public interface ThrowableConsumer<T, E extends Throwable>
> {
>    void accept(T t) throws E;
> }
>
> A method that receives this as a parameter could be defined as such...
>
> public <E extends Throwable> void process(ThrowableConsumer<T, E> consume)
> throws E
> {
>    ...
> }
>
> The compiler takes care of ensuring the checked exception is dealt with in
> the caller to process().
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20221112/146b6ecd/attachment-0001.htm>


More information about the amber-dev mailing list