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);
}

Reply via email to