Lambda return types and overloaded methods

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Tue Aug 16 16:41:59 UTC 2016



On 16/08/16 14:58, Greenaway, Chris wrote:
>
> I agree with you. After following your highlighting of those pieces of 
> 15.12.2.1, I can see why the examples I gave do not compile.
>
> However, there seems to be a conflict between 15.12.2.1 and 15.27.3.
>
> In 15.27.3, it says that a “lambda expression is compatible in an … 
> invocation context … with a target type T if T is a functional 
> interface type and the expression is congruent …” with T, in the case 
> under discussion.
>
I think (the spec gurus will correct me if I'm wrong) that 15.27.3 only 
happens when you look inside the body of a lambda (*). Whether you are 
allowed or not to look inside the body is dictated by 15.12.2.1.

(*) is not even that simple, as 15.27.3 is used only when the target 
type of a lambda is 'ground' i.e. w/o inference variables - if it has 
inference variables slight variation of 15.27.3 is used, namely 18.2.1.

Maurizio
>
> The definition of congruent specifies that where the function type’s 
> result is non-void, that the lambda must return a type compatible with 
> the function type’s result.
>
> Additionally, it feels inconsistent / unintuitive that “(String s) -> 
> s.trim()” is fine, but “s -> s.trim()” is not.
>
> Chris.
>
> *From:*Maurizio Cimadamore [mailto:maurizio.cimadamore at oracle.com]
> *Sent:* 16 August 2016 13:49
> *To:* Greenaway, Chris; compiler-dev at openjdk.java.net
> *Subject:* Re: Lambda return types and overloaded methods
>
> 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 
> <mailto: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/*//
>
> 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/3857ee18/attachment-0001.html>


More information about the compiler-dev mailing list