Aside: My understanding of the MethodParameters attribute is that it's intended more as an informational than API and unlikely to affect application behaviour at runtime.

The existing MP attribute represents a somewhat unfortunate compromise.

The EE folks (remember EE?) had a compelling-enough use case for being able to reflect over parameter names, which was that they wanted to generate various remote-invocation stubs (corba, *RPC, etc) from classes, but felt that having to annotate each parameter (`foo(@MyNameIs("bob") String bob)`) would suck.  So they wanted us to reify the parameter names by default in the classfile, for reflection to serve up.

The ME folks (remember ME?) freaked out; "Whoa, more bytes in the classfile, you have to make that optional, and not the default!"

The EE guys came back with "OK, how about driving retention based on an annotation."

We said (for the zillion'th time):  You know that's not what annotations are for.

So the compromise was to make it a tooling option; invoke `javac` with `-parameters`.   Result: no one can count on them.

Now, the MP attribute itself should not need to change here; only the conditions under which the language promises to make these available for reflection and condition the compatibility rules.
Making names significant and part of the API will affect refactoring and separate compilation: developers are used to being able to freely rename parameters without worrying about affects on other parts of their code base or about breaking their consumers (library authors).  Are there things the language can do to help with name transitions?  Is treating parameter name changes in a __byname method as a breaking API change something that developers will have to adapt to?

Note that we already have this problem with records; the name of a component in records flows into constructor descriptors, accessor names, etc.  It is an open question whether the developers can handle the responsibility :)

I have long wanted to have widely available, easy to use tooling that could detect incompatible changes between versions of a module, where it would look at the old bytecode and say "whoa, you did <incompatible thing> there, did you mean to?"  In the presence of such tooling, this would be one more set of assertions for such a mechanism.  But so far I haven't gotten my wish.  (Perhaps our IDE friends are cooking something like this up?)  I do think that detection of incompatible changes is something for tooling to handle, not the language.  But I agree that this is a concern, and more so than for records, which have the component names stapled to their forehead.
Similarly libraries that implement a common specification (Java EE, etc) have historically allowed each implementation to name the parameters in ways that made sense for their implementation.  I can see this becoming "names are API, live with it" which is a reasonable answer but one that will take some time to filter thru the ecosystem after the feature is released.  Any concerns that adopting this feature forces a "flag day" on consumers of such libraries with all implementors needing to move in lockstep?

I think the real challenge here is ensuring that, if the decision is made to make a member __byname, that they understand the consequences.  ("Nah, that could never change!", said every developer ever, incorrectly.)  Overuse of __byname could easily become a problem.  (I think also you're observing that this is in a sense worse than "names are API, live with it"; really, it's "names are *sometimes* API, live with that!"?)

A member going from insignificant to significant naming should be a compatible move, as should (eventually) adding a named parameter with a default to a constructor (though there's more work to do here to make this compatible.)  The rule about telescoping chains is there in part to allow us to evolve such members and leave the old entry points around.  The flag day is when we realize "crap, that name really is so terrible we have to change it."

In the "Refining overloading" section [2]
> Each __byname constructor / deconstructor can be invoked positionally, so it must follow > the existing rules for overloading and overload selection. A set of __byname members is > valid if and only if for each two such members, their name sets are either disjoint or one > is a proper subset of the other and the types corresponding to the common names are the same. This seems like an answer to the refactoring concerns above as it provides a way to leave previous API points around to not break consumers.  Will the normal deprecation schemes apply to these name-only overloads?  If so it covers most of the refactoring concerns - though the need to leave the old API points is a bit of code smell that may discourage some kinds of refactoring.  Time will tell.

Yes.  The real concern is (when we get to default parameters) that, if you have seventeen parameters already, that you'll have 18 tomorrow is virtually a guarantee.   This is tricky ground, I think I have a story though.
Is the intention that the VM would check the disjoint constraints during classloading?  I can see it being pushed to the VM to validate or left as a language level rule with the VM's resolution taking the first available match.  At first glance, both seem reasonable though VM validation will incur startup costs and would need more clarity on how checks for duplicate __byname constructors would mesh with the "Factories" proposal, either disjoint or combined checks?  Details to be worked out as the proposal progresses.

Asking the VM to check these seems a bit much, especially given that the names exist primarily for overload selection, which is a language concern.  My thinking for by-name invocation is that we do overload selection statically, pick a positional signature, and invoke with an indy that treats that positional signature as a fast path, but falls back to an ugly reflective linkage on fail.  This is the standard trick of "replicate the language semantics at runtime in indy" so the VM doesn't have to.


Reply via email to