=?utf-8?Q?Re:_Overload_resolution_chooses_type_that's_invalid, _too_genera?= l or not at all
timo.kinnunen at gmail.com
Mon Jan 13 16:17:52 PST 2014
Re: “I know what you want is to define two identical methods that simply vary in the exceptions thrown. The problem is that, for all the compiler knows, the two methods do dramatically different things, and the compiler is interested in minimizing the risk of accidentally getting the wrong behavior.
(What you mean, by the way, is "choose the one that throws the least ***but still declares all the exceptions thrown by the lambda body***".)”
It isn’t the compiler’s place to be the decider of which same-but-different things are OK and which are not. There is a dramatic difference between System.out.println("10") copying 4 bytes in memory and System.out.println(10) doing integer divisions yet there is no need for claiming ambiguity, because the difference is in implementation that the compiler can reason about and not in the Javadoc which the compiler doesn’t understand anyway.
And no, I don’t mean that. “But still declares all the checked exceptions” has to be checked anyway, regardless of how that type was arrived at. Explicitly listing more exceptions than the lambda block body can throw is valid, maybe with a warning. Doing this mandatory check on two types instead of one and then not picking a one that doesn’t violate the spec would be illogical.
Re: “The simplest solution is to simply give the methods different names.”
This would push the problem to the users of my API and is not the solution, because I am the user of my API.
Re: “May I suggest that what you really mean to say is this: the language doesn't handle exception transparency for lambda expressions very well? If so, then yes, we know, and have some ideas about how to address that problem, preferably entirely sidestepping overloading and generics.”
Not just for lambda expressions, I found that to be the case in regular methods as well, when implementing exception wrappers and unwrappers around Stream pipeline stages. Things like a way to catch and throw exceptions generically and being able to use multi-catch syntax in a throws specification would help, but these are dancing around the real problem.
What is needed is a way to insulate the implementation details of one method A from the implementation details of another method B that the method A calls, such that whatever happens after this crossover point - exceptions and all - happens as if it was the caller of method A who called method B.
This is Guy Steele’s tail-call optimization, I’ve just realized.
Have a nice day,
Sent from Windows Mail
From: Dan Smith
Sent: Tuesday, January 14, 2014 00:12
To: Timo Kinnunen
Cc: lambda-dev at openjdk.java.net
On Jan 10, 2014, at 4:05 AM, Timo Kinnunen <timo.kinnunen at gmail.com> wrote:
> In the case where the difference between two target types is only that one
> of them has a generic throws declaration, this leads to ambiguity even when
> choosing the wrong one would be a compile error.
> This seems to be working as specified, with the goal to make the result
> more stable and predictable, but I would argue the actual result is just
> the opposite. Why not try each candidate in turn and choose the one that
> throws the least?
"Do exactly what I want" and "be stable and predictable" are often in tension when the compiler is asked to infer something.
You're right that the treatment of exceptions thrown by a lambda body in overload resolution is by design, with the intent that ambiguity errors will prompt the user to be more explicit about what he wants (and prompt API designers to avoid these subtle overloads).
I know what you want is to define two identical methods that simply vary in the exceptions thrown. The problem is that, for all the compiler knows, the two methods do dramatically different things, and the compiler is interested in minimizing the risk of accidentally getting the wrong behavior.
(What you mean, by the way, is "choose the one that throws the least ***but still declares all the exceptions thrown by the lambda body***".)
> I have found a workaround. By making some small artificial changes (notice
> a pattern?) to one method that makes its signature less readable I can
> affect changes in which rounds each method participates. This allows things
> to compile but I lose some useful information about which lambdas actually
> don't throw exceptions. I have found a lot of variations to choose from
> but not the one that produces the optimal result so far.
The simplest solution is to simply give the methods different names.
> I'd like the compiler to choose the optimal method automatically, because
> it knows the types better than I do.
The problem is that the compiler doesn't know whether you really mean to throw an exception, or whether you intend to catch it but you forgot. And it doesn't know whether you really meant to change the program's behavior when you commented out a line that happened to throw an exception.
> Absent that, I'd like it to give each
> method its due consideration, because that's why I added the overloads in
> the first place. Absent that, I'd like some API for saying which order the
> overloaded methods should be tried. And absent that, I'd like to have some
> official documentation on how to correctly target methods to a particular
> round of resolution.
As an API designer, you make one method have priority over another by either i) making it more specific than another for expected inputs; or ii) making it applicable in an earlier phase for expected inputs. I acknowledge that you're looking for something more; it's not clear that this is a general enough problem to prompt language changes.
But in any case, I don't see how this helps. If you make the method that has a non-exception-throwing functional interface parameter more specific than the method that has an exception-throwing functional interface parameter, you're just going to get compiler errors whenever you have any exceptions. Both methods will always be applicable, because exceptions thrown by a lambda body do not influence method applicability.
May I suggest that what you really mean to say is this: the language doesn't handle exception transparency for lambda expressions very well? If so, then yes, we know, and have some ideas about how to address that problem, preferably entirely sidestepping overloading and generics.
More information about the lambda-dev