Lambda return types and overloaded methods

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Tue Aug 16 12:49:06 UTC 2016



On 16/08/16 13:38, Greenaway, Chris wrote:
>
> That doesn’t seem to fit with the discussion on this JDK bug: 
> https://bugs.openjdk.java.net/browse/JDK-8029718 which has been fixed. 
> In particular, there’s a link to a discussion on this mailing list ( 
> http://mail.openjdk.java.net/pipermail/lambda-dev/2013-November/011394.html 
> ) where various members express the opinion that the compiler should 
> consider more than just arity.
>
The bug (and the discussion) you refer to consider the case of value vs. 
void compatible lambda. I.e. if a lambda does not return, that 
information is enough to rule out a functional interface with non-void 
return type.
In your case the lambda is returning something, and both functional 
interfaces also return something. So that doesn't apply.
>
> An important consideration seems to be 15.27.3 of the JLS. 
> Paraphrasing, it says that a lambda with implicitly typed parameters 
> is only compatible with a functional interface if the lambda body 
> results in a value compatible with the functional interface’s result 
> type. The definition of potentially compatible in 15.12.2.1 
> specifically references this.
>
I don't see references from 15.12.2.1 to 15.27.3. I see however 
references to 15.27.2 - when the potentially compatible definition takes 
into account as whether the lambda is void- or value- compatible.

Maurizio
>
> Chris.
>
> *From:*Maurizio Cimadamore [mailto:maurizio.cimadamore at oracle.com]
> *Sent:* 16 August 2016 11:18
> *To:* Greenaway, Chris; compiler-dev at openjdk.java.net
> *Subject:* Re: Lambda return types and overloaded methods
>
> Hi Chris,
>
> what you are observing is by design - to keep things tractable, we 
> deliberately decided to keep overload resolution a function of the 
> argument expressions, and to only look at the return types after 
> inference. In fact, javac won't even look at the body of a lambda, or 
> a method reference or conditionals if such expressions are considered 
> _not_ pertinent to applicability (see JLS section 15.12.2.2 for a 
> formal definition). In short, an argument expression is pertinent to 
> applicability unless it is an implicit lambda (a lambda w/o explicit 
> parameter types) or an inexact method reference (a method reference 
> matching multiple overloads) - or a conditional whose either 
> sub-expression is one of those.
>
> So, when you have argument expressions that are not pertinent to 
> applicability, your only hope against ambiguity error is the loose 
> applicability check described in JLS 15.12.2.1 - potential 
> compatibility. This is a much looser check which simply validates that 
> the lambda (or method reference) has an arity that is compatible with 
> that of the target functional interface (for method reference we also 
> look at void vs. value compatibility).
>
> Sp, all your implicit lambda cases are ambiguous, because your targets 
> are Predicate<String> and Function<String, String> - i.e. both are 
> functional types accepting a String (arity 1). Given we are not 
> allowed to look at the lambda body at this stage (the lambda is not 
> pertinent to applicability), we have to give up.
>
> In the case of the method references, you have that both 
> String::isEmpty and String::trim are _exact_ method references - they 
> are unambiguous, so the return types of such methods can indeed be 
> used to disambiguate. The same cannot be said for String::toUpperCase 
> which has two matches - so both Predicate<String> and Function<String, 
> String> are potentially compatible with String::toUpperCase (given 
> that one of the overloads for String::toUpperCase matches the arity of 
> the function types) - but the method reference expression is then 
> discarded from further checking because it is an inexact method 
> reference, so the return type cannot be looked at.
>
> If this behavior seems too strict, please keep in mind that these 
> rules have been discussed extensively at the various lambda EG 
> meetings; at some point we had a more powerful overload resolution 
> algorithm - but there was an unanimous consensus to back out from it 
> because its behavior was very hard to explain to users (the compiler 
> would speculatively check a lambda body against each overloaded 
> method, and discard those not compatible - this leads to a 
> combinatorial explosion of checks in nested cases - and is generally 
> an hard-to-follow process for a non-stack based human ;-)).
>
> I concede that, in these particular cases, the behavior of pertinent 
> to applicability/potential compatibility is a little phony - in the 
> case of lambda, all overloads accept a functional interface which 
> dictate a String parameter - so, there seems to be no ambiguity as to 
> how the lambda body should be interpreted. A similar consideration 
> holds for the method reference case where, after all, both overloaded 
> toUpperCase methods return String, so why not using that information 
> to prune the space of applicable methods (since Predicate<String> 
> clearly does not return a String) ? While these might all be good 
> incremental improvements over what we have, I must stress that this is 
> not the language we have today.
>
> Cheers
> Maurizio
>
> (*) this is not totally correct - in fact return types are also used 
> for disambuguation when multiple methods are applicable - but for that 
> to apply the lambda/method reference must be pertinent to 
> applicability - i.e. an explicit lambda, or an exact method reference.
>
> On 16/08/16 10:21, Greenaway, Chris wrote:
>
>     There seems to be a problem with matching the return type of a
>     lambda to the type required by an overloaded method when the
>     lambda parameter isn't explicitly typed.
>
>     For example, the code below has compilation failures on the lines
>     that are marked with "// Error". All other lines are acceptable.
>
>     There also seems to be an issue with method references when there
>     are multiple methods with the same name but different arity - see
>     the line marked "// Error 8".
>
>     Chris Greenaway.
>
>     import java.util.function.Function;
>
>     import java.util.function.Predicate;
>
>     public class LambdaOverloading {
>
>     public static void main(String[] args) {
>
>     LambdaPredicate predicate = new LambdaPredicate();
>
>     predicate.run((String s) -> s.startsWith("X"));
>
>     predicate.run((String s) -> s.isEmpty());
>
>     predicate.run((String s) -> true);
>
>     predicate.run(s -> s.startsWith("X"));
>
>     predicate.run(s -> s.isEmpty());
>
>     predicate.run(s -> true);
>
>     predicate.run(String::isEmpty);
>
>     LambdaFunction function = new LambdaFunction();
>
>     function.run((String s) -> s.substring(1));
>
>     function.run((String s) -> s.trim());
>
>     function.run((String s) -> s.toUpperCase());
>
>     function.run((String s) -> "");
>
>     function.run(s -> s.substring(1));
>
>     function.run(s -> s.trim());
>
>     function.run(s -> s.toUpperCase());
>
>     function.run(s -> "");
>
>     function.run(String::trim);
>
>     function.run(String::toUpperCase);
>
>     LambdaOverloaded overloaded = new LambdaOverloaded();
>
>     overloaded.run((String s) -> s.startsWith("X"));
>
>     overloaded.run((String s) -> s.substring(1));
>
>     overloaded.run((String s) -> s.isEmpty());
>
>     overloaded.run((String s) -> s.trim());
>
>     overloaded.run((String s) -> s.toUpperCase());
>
>     overloaded.run((String s) -> true);
>
>     overloaded.run((String s) -> "");
>
>     overloaded.run(s -> s.startsWith("X"));                     // Error 1
>
>     overloaded.run(s -> s.substring(1));                        // Error 2
>
>     overloaded.run(s -> s.isEmpty());                           // Error 3
>
>     overloaded.run(s -> s.trim());                              // Error 4
>
>     overloaded.run(s -> s.toUpperCase());                       // Error 5
>
>     overloaded.run(s -> true);                                  // Error 6
>
>     overloaded.run(s -> "");                                    // Error 7
>
>     overloaded.run(String::isEmpty);
>
>     overloaded.run(String::trim);
>
>     overloaded.run(String::toUpperCase); // Error 8
>
>         }
>
>     private static class LambdaPredicate {
>
>     public boolean run(Predicate<String> predicate) {
>
>     return predicate.test("test");
>
>             }
>
>         }
>
>     private static class LambdaFunction {
>
>     public String run(Function<String, String> function) {
>
>     return function.apply("test");
>
>             }
>
>         }
>
>     private static class LambdaOverloaded {
>
>     public boolean run(Predicate<String> predicate) {
>
>     return predicate.test("test");
>
>             }
>
>     public String run(Function<String, String> function) {
>
>     return function.apply("test");
>
>             }
>
>         }
>
>     }
>
>     This communication contains information which is confidential and
>     may also be privileged. It is for the exclusive use of the
>     intended recipient(s). If you are not the intended recipient(s),
>     please note that any distribution, copying, or use of this
>     communication or the information in it, is strictly prohibited. If
>     you have received this communication in error please notify us by
>     e-mail and then delete the e-mail and any copies of it.
>
>     Software AG (UK) Limited Registered in England & Wales 1310740 -
>     */http://www.softwareag.com/uk/*//
>
> This communication contains information which is confidential and may 
> also be privileged. It is for the exclusive use of the intended 
> recipient(s). If you are not the intended recipient(s), please note 
> that any distribution, copying, or use of this communication or the 
> information in it, is strictly prohibited. If you have received this 
> communication in error please notify us by e-mail and then delete the 
> e-mail and any copies of it.
> Software AG (UK) Limited Registered in England & Wales 1310740 - 
> /*http://www.softwareag.com/uk* /
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20160816/01041010/attachment-0001.html>


More information about the compiler-dev mailing list