generics vs. checked exceptions

John Rose john.r.rose at oracle.com
Wed Mar 16 17:36:08 UTC 2022


Negora’s use case for demotion (temporary erasure, suppression) of 
checked exceptions is streams.  Streams are designed (quite nicely I 
might add) on top of Java generics.  But generics are (very consciously) 
limited in their interactions with checked exceptions:  There is no way 
to treat exception *sets* as generic parameters.

You can treat individual exception types as generic parameters but this 
doesn’t scale to lambda bodies which throw two checked exceptions, or 
(less so) no checked exceptions.  Exception tracking (including 
“inferring” the throws of a lambda body) interoperates to some 
extent with generic type inference, allowing single exception types to 
be inferred and bound (constrained) to type variables, but the inference 
rules don’t provide well other than a single exception.

In my mind this weakness of generics relative to checked exceptions is 
similar to the weakness of generics relative to primitives.  (Also the 
weakness in not supporting variable-arity type abstraction; it’s hard 
but doable.)  And, in my mind, such weaknesses are ones we could fix 
somehow, at some point when we have the bandwidth.  But they are not 
simple to fix:  You don’t just propose a syntax and say “job 
done”.  The really hard part is specifying how the new feature 
interacts with (pretty much) *every single pre-existing feature*.  Oh, 
and it has to be backward compatible.  And unsurprising to users.  And 
fully specified with no gray areas.  And more…

I note that both generics and exceptions are erased in translation to 
classfiles; both depend on static checks in javac to maintain 
correctness.  This suggests (to me) that some sort of erasable, 
statically checks inference of exception *sets* would be helpful.  I 
think Ron Pressler has mentioned this point as well somewhere.

The erasure in translation also unlocked a wide variety of retrofitting 
moves when generics were introduced.  Even at this late date if we were 
to layer in a way to abstract over exception sets, we could hope (with a 
little like) to find that we could retrofit streams to handle checked 
exceptions.  This would address Negora’s point (about streams) far 
better than adding a new flavor of throws clause and/or lambda.

If we could stick a clause like “throws X” in various places, both 
uses and defs of generic type variables, where X somehow was taken to 
denote a possibly-empty set of checked exceptions, and then do the hard 
work to specify the inference rules (with all the constraints mentioned 
above) we might have a road towards making streams (and other APIs) 
friendlier to checked exceptions.  A stream would be retrofitted with 
some sort of “throws X” variable which would advertise what the 
terminal operations might throw, and various transforms would accumulate 
exceptions.  A new stream operation (non-terminal) for capturing and 
transforming exceptions might be added; if : filter :: catch : <the new 
thing>.  (I didn’t say “monad” there but I could have.)

All that said, I don’t want to work on it now, because I have far more 
pressing things to get done: pressing in a good, exciting, and fruitful 
way.  I imagine Brian has similar feelings.

(Idle question:  Does fruitful pressing eventually lead to good 
vintages?  One hopes!)

— John

P.S. On one of my many back burners, I’ve been experimenting with 
exposing checked exceptions through Java’s *existing* generics 
(nothing new) to find the limits of what they can do.  Here’s a 
snapshot, FWIW, of small number of types which expose not only the 
possible normal result value of an expression, but also its possible 
exceptional result:

http://cr.openjdk.java.net/~jrose/draft/exbox-javadoc

It’s not done.  But I find it thought-provoking and others might as 
well.


More information about the amber-dev mailing list