Trouble inferring type of lambda

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Thu Aug 26 01:18:15 PDT 2010


On 26/08/10 07:46, Howard Lovatt wrote:
> Hi Maurizio,
>
> Not sure if the compiler should cope but the current one doesn't:
>
>        il.<Exception>forEach( #( i ) { throw new Exception(); } ); //
> Need to catch the checked exception!
>    
Hi Howard,
this is similar to another example we discussed - the inferred return 
type is 'void' and, since return type and argument type should match 
(same type-variable in forEach() signature) inference fails here.

However I noticed that there's a glitch - from your error message I have 
inferred that maybe you are compiling against this signature of forEach 
(again, please post full source, or at least all relevant bits):

<R, throws E> void forEach( Method1<R, T, E> method ) throws
E;

In that case, the compiler should reject a call such as:

il.<Exception>forEach( #( i ) { throw new Exception(); } ); //Need to 
catch the checked exception!

because there are not enough explicit type-parameters in the method call 
- I guess that a nil-ary type-parameter is being detected here, but an 
explicit 'void' should be used for that one. That's what's causing the 
compiler to fail. If you remove the explicit type as in:

il.forEach( #( i ) { throw new Exception(); } ); //Need to catch the 
checked exception!

The program compiles fine.

Maurizio
> Gives:
>
> lambdas/Main.java:163: method forEach in class IntList11 cannot be
> applied to given types
>        il.<Exception>forEach( #( i ) { throw new Exception(); } ); //
> Need to catch the checked exception!
>          ^
>    required: Method1<Integer,Integer,E>
>    found: #void(?)(Exception)
>    where E is a type-variable:
>      E extends Exception declared in method
> <E>forEach(Method1<Integer,Integer,E>)
> 1 error
>
> And:
>
>        il.forEach( Method1<Integer, Integer, Exception>  #( i ) { throw
> new Exception(); } ); // Need to catch the checked exception!
>
> Gives:
>
> lambdas/Main.java:163: incompatible types; no instance(s) of type
> variable(s) ? exist so that #void(?)(Exception) conforms to
> Method1<Integer,Integer,Exception>
>        il.forEach( Method1<Integer, Integer, Exception>  #( i ) { throw
> new Exception(); } ); // Need to catch the checked exception!
>                    ^
>    required: Method1<Integer,Integer,Exception>
>    found:    #void(?)(Exception)
> 1 error
>
> So are these compiler bugs or is some other form of qualification possible.
>
> Cheers,
>
>   -- Howard.
>
> On 25 August 2010 18:08, Maurizio Cimadamore
> <maurizio.cimadamore at oracle.com>  wrote:
>    
>> On 25/08/10 06:00, Howard Lovatt wrote:
>>      
>>> Having tried:
>>>
>>> @Override public<R, throws E>    void forEach( final Method1<R, Integer,
>>> E>    method ) throws E {
>>>     for ( int i = 0; i<    values.length; i++ ) {
>>>       final Integer oldValue = values[ i ];
>>>       final R newValueTemp = method.call( oldValue );
>>>       if ( newValueTemp instanceof Integer ) { values[ i ] = (Integer)
>>> newValue; }
>>>       else { throw new IllegalArgumentException(); }
>>>     }
>>>   }
>>>
>>> To overcome the problem that:
>>>
>>>    list.forEach( #( i ) { throw new Exception() } );
>>>
>>> Wouldn't normally work (because the lambda returns void and not
>>> Integer). The problem with the above forEach implementation is that
>>> the return type is unconstrained and hence effectively unchecked by
>>> the compiler and generates runtime exceptions.
>>>
>>> Therefore the best I have come up with is:
>>>
>>>    list.forEach( #( i ) {
>>>      if ( alwaysTrue() ) { throw new Exception(); }
>>>      return 0;
>>>    } );
>>>
>>> Which I am not really satisfied with, is there better?
>>>
>>>        
>> The only option would be to provide explicit type-parameters in the method
>> call, as in:
>>
>> list.<Integer,Exception>forEach(...);
>>
>> Maurizio
>>
>>
>>
>>      
>>> Cheers,
>>>
>>>   -- Howard.
>>>
>>>
>>> On 23 August 2010 22:49, Howard Lovatt<howard.lovatt at gmail.com>    wrote:
>>>
>>>        
>>>> Thanks for the reply.
>>>>
>>>> Whilst this will work, it is hardly desirable. Effectively the return
>>>> type of all lambdas has to 'free' variable then writing a method that
>>>> uses them is awkward, e.g.:
>>>>
>>>>   @Override public<R, throws E>    void forEach( final Method1<R,
>>>> Integer, E>    method ) throws E {
>>>>     for ( int i = 0; i<    values.length; i++ ) {
>>>>       final Integer oldValue = values[ i ];
>>>>       final R newValueTemp = method.call( oldValue );
>>>>       if ( newValueTemp instanceof Integer ) { values[ i ] = (Integer)
>>>> newValue; }
>>>>     }
>>>>   }
>>>>
>>>> Not great!
>>>>
>>>> Cheers,
>>>>
>>>>   -- Howard.
>>>>
>>>> On 23 August 2010 21:59, Maurizio Cimadamore
>>>> <maurizio.cimadamore at oracle.com>    wrote:
>>>>
>>>>          
>>>>> On 23/08/10 12:34, Howard Lovatt wrote:
>>>>>
>>>>>            
>>>>>> Thanks for the clarification, I think it *should* be a bug!
>>>>>>
>>>>>> In the meantime, what is the recommended idiom for writing such a
>>>>>> function.
>>>>>>
>>>>>>
>>>>>>              
>>>>> You could try with:
>>>>>
>>>>> interface SortableList<T extends Comparable<? super T>>    extends List<T>
>>>>>   {
>>>>>
>>>>> <R, throws E>    void forEach( Method1<R, T, E>    method ) throws E;
>>>>>
>>>>> }
>>>>>
>>>>> this should do.
>>>>>
>>>>> Maurizio
>>>>>
>>>>>
>>>>>            
>>>>>>   -- Howard.
>>>>>>
>>>>>> On 23 August 2010 21:15, Maurizio Cimadamore
>>>>>> <maurizio.cimadamore at oracle.com>      wrote:
>>>>>>
>>>>>>
>>>>>>              
>>>>>>> On 23/08/10 12:02, Howard Lovatt wrote:
>>>>>>>
>>>>>>> Hi Mauritzio,
>>>>>>>
>>>>>>> I think it is a bug, like I said in the original post the 'return
>>>>>>> type' is 'NeverReturns' not void, which should be assignable to
>>>>>>> anything (including Integer). Scala and BGGA handle this case, they
>>>>>>> both have a 'NeverReturns' type (Scala and BGGA Nothing).
>>>>>>>
>>>>>>>
>>>>>>> Let me clarify - it is not a bug, according to the current spec;
>>>>>>> quoting
>>>>>>> from the latest formal spec describing lambda conversion [1]:
>>>>>>>
>>>>>>> "A divergent lambda expression such as #(){throw new
>>>>>>> AssertionError();}
>>>>>>> has no return value, and its body completes abruptly by reason of a
>>>>>>> throw with an AssertionError object. For the purpose of calculating
>>>>>>> the type of the lambda expression, the body of the lambda expression
>>>>>>> is void."
>>>>>>>
>>>>>>> [1] -
>>>>>>>
>>>>>>>
>>>>>>> http://mail.openjdk.java.net/pipermail/lambda-dev/attachments/20100212/af8d2cc5/attachment-0001.txt
>>>>>>>
>>>>>>> Maurizio
>>>>>>>
>>>>>>> Cheers,
>>>>>>>
>>>>>>>   -- Howard.
>>>>>>>
>>>>>>> On 23 August 2010 20:35, Maurizio Cimadamore
>>>>>>> <maurizio.cimadamore at oracle.com>      wrote:
>>>>>>>
>>>>>>>
>>>>>>> This is not a bug. You are trying to SAM convert the following lambda:
>>>>>>>
>>>>>>> #(x) { throws Exception(); }
>>>>>>>
>>>>>>> into the following target method:
>>>>>>>
>>>>>>> <throws E>      Integer call( Integer a1 ) throws E
>>>>>>>
>>>>>>> This is not possible, since the lambda expression is inferred to yield
>>>>>>> 'void' (no return expression found), while the target method returns
>>>>>>> Integer.
>>>>>>>
>>>>>>> Maurizio
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 22/08/10 18:45, maurizio cimadamore wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 22/08/2010 07:09, Howard Lovatt wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> For:
>>>>>>>
>>>>>>>     public interface Method1<R, A1, throws E>         { public R call( A1
>>>>>>> a1 )
>>>>>>> throws E; }
>>>>>>>
>>>>>>> and:
>>>>>>>
>>>>>>>     public<throws E>         void forEach( final Method1<Integer,
>>>>>>> Integer, E>
>>>>>>> method ) throws E { ... }
>>>>>>>
>>>>>>> The compiler has trouble with:
>>>>>>>
>>>>>>>      il.forEach( #( i ) { throw new Exception(); } ); // Need to catch
>>>>>>> checked exception
>>>>>>>
>>>>>>> Giving:
>>>>>>>
>>>>>>> lambdas/Main.java:34: method forEach in class IntList11 cannot be
>>>>>>> applied to given types
>>>>>>>         il.forEach( #( i ) { throw new Exception(); } ); // Need to
>>>>>>> catch checked exception
>>>>>>>           ^
>>>>>>>     required: Method1<Integer,Integer,E>
>>>>>>>     found: #void(?)(Exception)
>>>>>>>     where E is a type-variable:
>>>>>>>       E extends Exception declared in method
>>>>>>> <E>forEach(Method1<Integer,Integer,E>)
>>>>>>>
>>>>>>> Qualifying doesn't help:
>>>>>>>
>>>>>>>     Method1<Integer, Integer, Exception>         #( i ) { throw new
>>>>>>> Exception();
>>>>>>> }
>>>>>>>
>>>>>>> It gives:
>>>>>>>
>>>>>>> lambdas/Main.java:34: incompatible types; no instance(s) of type
>>>>>>> variable(s) ? exist so that #void(?)(Exception) conforms to
>>>>>>> Method1<Integer,Integer,Exception>
>>>>>>>         il.forEach( Method1<Integer, Integer, Exception>         #( i ) {
>>>>>>> throw
>>>>>>> new Exception(); } ); // Need to catch checked exception
>>>>>>>                     ^
>>>>>>>     required: Method1<Integer,Integer,Exception>
>>>>>>>     found:    #void(?)(Exception)
>>>>>>>
>>>>>>> Is the problem that the return type isn't expressible? It isn't really
>>>>>>> void, it is 'NeverReturns'.
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> Uhmm the problem here seems more related with a failure in inference
>>>>>>> of
>>>>>>> the lambda parameter (the '?' that you are seeing). Again it would be
>>>>>>> helpful to see the declaration of the receiver class as well as the
>>>>>>> type
>>>>>>> of 'il'.
>>>>>>>
>>>>>>> Maurizio
>>>>>>>
>>>>>>>
>>>>>>>                
>>>>>
>>>>>            
>>>>
>>>> --
>>>>    -- Howard.
>>>>
>>>>
>>>>          
>>>
>>>
>>>        
>>
>>      
>
>
>    



More information about the lambda-dev mailing list