inference of throws type parameters in SAMs

Peter Levart peter.levart at gmail.com
Thu Nov 1 02:13:55 PDT 2012


On 10/31/2012 10:48 PM, Maurizio Cimadamore wrote:
> On 31/10/12 20:51, Peter Levart wrote:
>> On 10/31/2012 06:48 PM, Maurizio Cimadamore wrote:
>>> On 31/10/12 17:37, Remi Forax wrote:
>>>> On 10/31/2012 06:20 PM, Maurizio Cimadamore wrote:
>>>>> On 31/10/12 17:04, Remi Forax wrote:
>>>>>> On 10/31/2012 03:53 PM, Dan Smith wrote:
>>>>>>> This is on the radar for inference. As you suggest, we would
>>>>>>> probably just choose RuntimeException in certain scenarios rather
>>>>>>> than using the upper bound.
>>>>>>>
>>>>>>> We have toyed with some more ambitious ideas for handling
>>>>>>> exceptions in lambda bodies, but those won't be part of Java 8.
>>>>>>>
>>>>>>> —Dan
>>>>>> yes, and my fear is that if we choose something different than 
>>>>>> Object
>>>>>> now (like RuntimeException)
>>>>>> we will not be able to change the inference algorithm in Java 9.
>>>>>> At least with Object as default, we know that currently the 
>>>>>> inference
>>>>>> fails so
>>>>>> we can provide a better one without creating incompatibilities.
>>>>> I think Object is never a problem - for this kind of issues to show
>>>>> up, the inference variable must appear in the throws clause, which
>>>>> means it should be at least <: Throwable.
>>>> yes, sorry,
>>>> s/Object/Throwable
>>>>
>>>> Choosing the bound of the unconstrained variable if the variable is
>>>> used in a throws in not the better choice,
>>>> but before we came with a better algorithm, it's a better choice than
>>>> RuntimeException because it doesn't
>>>> hamper the use of a better algorithm in the future.
>>> Yeah - I think that's kinda the choice ahead of us - on the one 
>>> hand, if
>>> we add exception transparency support in the future, it seems like this
>>> case should be handle as a result of that work; on the other hand,
>>> inferring RuntimeException could be a reasonable compromise, esp. given
>>> that the number of incompatibilities that will occur as a result of an
>>> inference upgrade would be pretty low (though it's true that when
>>> lambdas are available inference variables in the throws clause might
>>> become more common...).
>>>
>>> Maurizio
>> Can we elaborate about incompatibilities that will occur as a result 
>> of later inference upgrade?
>>
>> If I understood correctly, you are talking about inference upgrade 
>> that will happen after JDK8 and which will contain exception 
>> transparency support.
>>
>> So, unconstrained variable that is used in functional interface and 
>> appears only in throws declaration of the sole interface method. What 
>> incompatibilities can it cause if it is inferred now as 
>> RuntimeException as opposed to upper bound and in a later release as 
>> something more appropriate like Nothing? Can we imagine an example?
>
> An example would be along the following lines (admittedly convoluted):
>
> interface SAM<X extends Throwable> {
>     void m() throws X;
> }
>
> <Z> List<Z> m(SAM<Z> s) { ... }
>
> g(List<RuntimeException> l) { } //1
> g(Object o) { } //2
>
> g(m(()->{})); //resolves to (1) in JDK 8, resolves to (2) in JDK x+1, 
> x >= 8 ;-)
>

That would be the case if JDK 8 inferred RuntimeException in certain 
unconstrained scenarios rather that upper bound.

That's true, but so is true that a modified example would suffer form 
the same incompatibility if JDK8 inferred upper bound in such scenarios 
(Throwable in this example):

g(List<Throwable> l) { } //1
g(Object o) { } //2

g(m(()->{})); //resolves to (1) in JDK 8, resolves to (2) in JDK x+1, x 
 >= 8

This example is bad API. More correctly written API would use:

g(List<? extends Throwable> l) { } //1
g(Object o) { } //2

Which is less likely to suffer from an incompatibility - for example if 
inference upgrade ever used say type Nothing and inferred List<Nothing> 
in such cases.

Regards, Peter

> Maurizio
>
>>
>> Regards, Peter
>>
>>>>> Maurizio
>>>> Rémi
>>>>
>>>>>> Rémi
>>>>>>
>>>>>>> On Oct 30, 2012, at 10:18 AM, Peter Levart <peter.levart at gmail.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> Hi all,
>>>>>>>>
>>>>>>>> I know this has been discussed before, but a long time has 
>>>>>>>> passed and
>>>>>>>> various inference algorithms have been tried in the meanwhile. 
>>>>>>>> So I
>>>>>>>> would like to ask whether current state is final. For example if I
>>>>>>>> have
>>>>>>>> a functional interface like the following:
>>>>>>>>
>>>>>>>> public interface ThrowingFactory<T, E extends Throwable> {
>>>>>>>>        T make() throws E;
>>>>>>>> }
>>>>>>>>
>>>>>>>>
>>>>>>>> And a "hypothetical" method:
>>>>>>>>
>>>>>>>> public interface Stream<T> {
>>>>>>>>        <E extends Throwable> T 
>>>>>>>> findFirstOrElse(ThrowingFactory<T, E>
>>>>>>>> other) throws E;
>>>>>>>> }
>>>>>>>>
>>>>>>>>
>>>>>>>> Then the following program compiles OK:
>>>>>>>>
>>>>>>>>        public static void main(String... args) throws IOException
>>>>>>>>        {
>>>>>>>>            Stream<String> stream = null;
>>>>>>>>
>>>>>>>>            String first = stream.findFirstOrElse(() -> {throw new
>>>>>>>> IOException();});
>>>>>>>>        }
>>>>>>>>
>>>>>>>>
>>>>>>>> Which indicates that the inference algorithm does it's job 
>>>>>>>> correctly.
>>>>>>>> But the following program:
>>>>>>>>
>>>>>>>>        public static void main(String... args)
>>>>>>>>        {
>>>>>>>>            MyStream<String> stream = null;
>>>>>>>>
>>>>>>>>            String first = stream.findFirstOrElse(() -> "NO 
>>>>>>>> VALUE");
>>>>>>>>        }
>>>>>>>>
>>>>>>>>
>>>>>>>> Triggers the compilation failure:
>>>>>>>>
>>>>>>>> error: unreported exception Throwable; must be caught or declared
>>>>>>>> to be
>>>>>>>> thrown
>>>>>>>>            String first = stream.findFirstOrElse(() -> "NO 
>>>>>>>> VALUE");
>>>>>>>>
>>>>>>>>
>>>>>>>> Regards, Peter
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> P.S. If this worked, the controversial TypeThatMustNotBeNamed<T>
>>>>>>>> which
>>>>>>>> is used as return type of 3 Stream methods could be replaced with
>>>>>>>> 3*3=9
>>>>>>>> Stream methods that would more or less fit the main purpose of 
>>>>>>>> being
>>>>>>>> fluent. For example, current Stream.findFirst() would expand to:
>>>>>>>>
>>>>>>>> public interface Stream<T> {
>>>>>>>>        T findFirst() throws NoSuchElementException;
>>>>>>>>        T findFirstOrElse(T other);
>>>>>>>>        <E extends Throwable> T 
>>>>>>>> findFirstOrElse(ThrowingFactory<T, E>
>>>>>>>> other) throws E;
>>>>>>>> }
>>>>>>>>
>>>>>>>> Is this to much methods?
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>
>>
>



More information about the lambda-dev mailing list