Retiring Checked Exceptions Was: Throwing Functions

John Hendrikx hjohn at xs4all.nl
Sat Nov 12 16:16:10 UTC 2022


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>
>>To: "amber-dev" <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/42f04143/attachment.htm>


More information about the amber-dev mailing list