Proposal: Infer checked exceptions from private methods

negora public at negora.com
Mon Mar 7 07:55:39 UTC 2022


Thank you for your answer, Brian.

 > When we did type inference for local variables, we deliberately
 > restricted it to local variables only, not fields or methods, because
 > methods and fields are part of a classes API, and we wouldn't want the
 > API to subtly change based on implementation detail, which would cause
 > linkage errors.

That's a good reason. But that's why this type of inference would be 
limited to private methods. Non-private methods would act as barriers to 
prevent unwanted changes in the public API (I use the term _public_ in 
the broadest sense).

 > And we did have a reason for that: simplicity.  By not coupling
 > inference to complex other details such as accessibility, it is easier
 > to reason about when inference can be used (developers hate when they
 > can't easily understand which features can be used where),

That seems reasonable. But, when you limit inference to the local scope, 
Aren't you also coupling that feature with another detail: variable 
scope in this case?

 > Supporting inference of exceptions here is very similar to inference
 > of return type.

Sincerely, I don't like type inference in return types either. But there 
is something on checked exceptions that I see different from classic 
returns: you can't forget to handle or propagate a checked exception, 
because you're forced to do something with it. You're also forced to 
know the type of the exception. In this case you would be forced to, at 
least, use `throws uncaught`.

In contrast, you may forget to return a value and the compiler wouldn't 
warn you until you tried to assign the return type to a variable. Or, 
more probably, you may return a type different from the one that you 
expected.

 > Such a feature is also likely to be misinterpreted; not all developers
 > will immediately realize this is mere inference, they may well think
 > it is "turning off checked exceptions" (and many of them will want to
 > believe this.)

But this kind of misinterpretations also happened with other features. 
For example, with `var`, many people thought that you were giving up on 
static typing and using dynamic typing.

 > Which, for the vocal segment of Java users who think
 > checked exceptions are the worst thing ever, would seem like a
 > big slap in the face -- "you let me turn off checked exceptions,
 > but only in the least important places!"

Hehehe. This is funny, because I've another proposal which is related to 
what you comment here. But I prefer to talk about it separately, in 
another thread. Because it's not directly related to this one.

Going back to the main topic, I understand your concerns. But how can we 
make it more comfortable to work with checked exceptions? Checked 
exceptions are a valuable feature, in my opinion. But I also think that 
we should count with some aid to make them more "digestible".

I've also thought on type aliases, but limited to the class internal 
scope. That way you could assign an union of exceptions to a name and 
throw that. The outer world (including the Javadocs) would only see the 
union, as always. But I feel that type aliases are even a more 
controversial feature.


On 03/03/2022 22:36, Brian Goetz wrote:
> While I understand the motivation for such a request, I suspect it would 
> likely make things worse, not better.
> 
> When we did type inference for local variables, we deliberately 
> restricted it to local variables only, not fields or methods, because 
> methods and fields are part of a classes API, and we wouldn't want the 
> API to subtly change based on implementation detail, which would cause 
> linkage errors.
> 
> Clever folks observed: "but AHA, you could use inference for *private* 
> method returns and fields without having that problem, why wouldn't you 
> do that?"
> 
> And we did have a reason for that: simplicity.  By not coupling 
> inference to complex other details such as accessibility, it is easier 
> to reason about when inference can be used (developers hate when they 
> can't easily understand which features can be used where), and more 
> importantly, means that making a private method public is simply a 
> matter of changing the accessibility, not rewriting the method header.  
> Ad-hoc interactions between features (e.g., inference and accessibility) 
> is a pernicious form of hidden complexity.
> 
> Supporting inference of exceptions here is very similar to inference of 
> return type.
> 
> Such a feature is also likely to be misinterpreted; not all developers 
> will immediately realize this is mere inference, they may well think it 
> is "turning off checked exceptions" (and many of them will want to 
> believe this.)  Which, for the vocal segment of Java users who think 
> checked exceptions are the worst thing ever, would seem like a big slap 
> in the face -- "you let me turn off checked exceptions, but only in the 
> least important places!"
> 
> On 3/3/2022 8:08 AM, negora wrote:
>> Hi:
>>
>> I've seen many programmers propagate all checked exceptions up the 
>> call stack,
>> from multiple private methods to the first non-private method of the 
>> class.
>> There, some exceptions are allowed to propagate, whereas others are 
>> wrapped
>> with one or more unchecked exceptions.
>>
>> So I would like to propose a variation of the `throws` clause, such as 
>> `throws
>> uncaught` or `throws *`, so that the compiler infers which checked 
>> exceptions
>> are thrown. **This feature would be limited to private methods,** so that
>> developers are forced to think about their public APIs and don't let
>> implementation details leak.
>>
>> Thanks to this, propagating exceptions inside a class would be super 
>> easy and
>> would not clutter the code so much. I work in big server applications 
>> and some
>> times the list of exceptions to "carry" between private methods is so 
>> long,
>> that even the simplest method takes multiple lines of code, making it 
>> hard to
>> read.
>>
>> In addition to this, if you changed the implementation in the future, 
>> you would
>> have not to worry much about the exceptions thrown by the private 
>> methods,
>> because you could make the necessary changes only in the non-private 
>> methods.
>>
>> Example with irrelevant parts omitted:
>>
>> ```
>> /* This method does nothing special. */
>> public void addStudentToRepository (
>>     String name,
>>     String surname
>>     Integer schoolID)
>>     throws SchoolNotFoundException {
>>
>>   try {
>>
>>     ...
>>     School school = findSchool (schoolID);
>>     ...
>>
>>   } catch (UnavailableRepositoryException | WrongIDTypeException ex) {
>>     throw new ImplementationException (ex);
>>   }
>>
>> }
>>
>> /* Here is the interesting part. */
>> private School findSchool (Integer id)
>>     throws uncaught {
>>         /* ^^^ This is equivalent to throw
>>          * "UnavailableRepositoryException",
>>          * "WrongIDTypeException",
>>          * and "SchoolNotFoundException" separately.
>>          */
>>
>>   School school = repository.findObject (id, School.class);
>>                           /* ^^^ This throws
>>                            * "UnavailableRepositoryException"
>>                            * and  "WrongIDTypeException".
>>                            */
>>   if (school != null) {
>>     return school;
>>   } else {
>>     throw new SchoolNotFoundException (id);
>>   }
>>
>> }
>> ```
>>
>> This example is very basic. I know that 
>> `UnavailableRepositoryException` and
>> `WrongIDTypeException` could inherit from a common 
>> `RepositoryException`, but:
>>
>> 1. You lose the specific types of the exceptions.
>>
>> 1. Sometimes that's not an option, because the exceptions come from 
>> different
>> libraries or frameworks.
>>
>> Thank you!
>>
>> Best regards,
>> negora.
> 


More information about the amber-dev mailing list