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.