Migrating methods in Collections
Brian Goetz
brian.goetz at oracle.com
Mon Dec 21 18:03:42 UTC 2015
> Instead of demoting, say, remove(int) to a partial method, simply
> hide it from all source level 10 code, which will only be able to access
> removeAt, even on a List<String> (the method will still be in the class,
> of course). Cons: breaks source compatibility (but not binary
> compatibility) in a more major way than ever before, but Java has
> mechanisms to deal with that (source level), and automatic migration
> tools should be easy. Pros: less strange than partial methods; simpler
> to implement; a more general (albeit crude) migration mechanism, or,
> rather binary-compatible source-deprecation mechanism.
I think people would be pretty ticked off if Map.get() just went away.
I think the response would be: "Those idiots decided to change their
libraries for their own reasons, I have no intention of ever
specializing my Map, and yet I have to change my code anyway."
Secondarily, while we might plan to do this to Collections in version N,
other generic libraries (including other JDK libraries) might wait until
N+3 to anyfy their own. Realistically this means we'd be forced to
expose whatever versioning mechanism we use here for general use --
which seems at least as potentially confusing (and open to misuse) as
partial methods. While a method-grained versioning mechanism seems like
it might solve a lot of problems (for example, we wouldn't have needed
to do default methods), so far, we've not seen any satisfactory theory
that we'd want to consider building on -- there have been many attempts
in the academic literature but I think method versioning in object
oriented systems is still an unsolved problem. So I'm wary this could
degenerate into something far worse than partial methods -- a bad
versioning system.
Separately, I think the distaste for partial methods may also be a
little bit an allergic reaction to the deliberately-bad syntax we're
using. I'll share a caricature of a past interaction on this topic
(with someone on this list, actually) that illustrates the power of
implicit syntactic biases:
Him: This where-clause thing is totally confusing and will be completely
foreign to Java developers! Augh!
Me: What if I wrote it like this instead:
boolean remove(Collection<ref T> this,
int index)
Is that less confusing? (oh, and BTW this builds on the *existing Java
8 syntax* that is already there for explicit receiver parameters, which
we added so they can be annotated.)
Him: That's so much better! Then its clear that the restriction is just
part of the method signature. And if there is more than one partial
method called foo(), its clear from this that they are distinct overloads.
Now, I don't want to devolve into premature syntax bikeshedding, but my
point is: I don't think the it is the concept that is fundamentally
confusing, its just that we will (in addition to convincing ourselves
that the model is sound, which is the task currently in front of us)
then additionally have to fit it into a syntactic expression that makes
sense to Java users. (Coming up with a good syntactic form is also
hard, so I want to first ensure we have a sound theoretical model before
taking on unnecessary additional work.)
> Now, it is a dramatic break, but Valhalla is quite dramatic anyway.
> Partial methods are a migration measure (we wouldn’t have needed them
> had the APIs been designed with values in mind, right?) but they’ll stay
> a part of the language forever, and they don’t have the general
> usefulness of default methods (unless there are non-migration reasons to
> make use of partial methods that make sense in Java).
Mostly, but not entirely. Partial methods also allow you to do this:
interface List<any T> {
<where T=int> default long sum() { ... }
}
which is not strictly related to migration. (Personally, I don't love
this as a feature, because it's weaker than it first appears (think:
"The Expression Problem"), and when you try to shore up these weaknesses
with a more powerful slicing mechanism like <where T extends Numeric> it
starts to get more complex -- but this form of partial method is also
part of the current best approach we've got for being able to replace
IntStream with Stream<int>, which is easier in some ways, and harder in
others, than Collections.)
However (picking up the above syntactic form), would you find these
signatures terribly confusing?
interface Stream<any T> {
...
int sum(Stream<int> this);
long sum(Stream<long> this);
double sum(Stream<double> this);
}
More information about the valhalla-spec-experts
mailing list