Trouble inferring type of lambda

Howard Lovatt howard.lovatt at gmail.com
Thu Aug 26 02:44:20 PDT 2010


Hi Maurizio,

It is the same source as before but to save you looking things up I
have repeated the relevant bits below:

public interface Method1<R, A1, throws E> { public R call( A1 a1 ) throws E; }

public class IntList11 extends AbstractList<Integer> implements
SortableList<Integer> {
  ...
  @Override public <throws E> void forEach( final Method1<Integer,
Integer, E> method ) throws E { ... }
}

I am not wanting to use (your suggestion):

  public <R, throws E> void forEach( final Method1<R, Integer, E>
method ) throws E { ... }

because R can now be anything and hence prevents the compiler catching
type errors (see earlier posts for more details). If I add R it does
indeed compile, but as I said I find the 'cure' worse than the
'problem'. However I am not satisfied with 'my' solution either:

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

and hence still searching for something better.

Therefore:

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

is correct for my signature, the only generic parameter is E, and:

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

doesn't compile and I also don't seem to be able to qualify the
statement to 'help' the compiler out (as shown previously).

Your responses are much appreciated,

 -- Howard.

On 26 August 2010 18:18, Maurizio Cimadamore
<maurizio.cimadamore at oracle.com> wrote:
> 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.
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>>
>
>



-- 
  -- Howard.


More information about the lambda-dev mailing list