All of this, and: what happens when a client compiles against a given
named method signature, and then, through separate compilation, the
method signature changes, say, to add a new parameter? Dealing with
this in some form seems essential, as, the most common cases where you
want to invoke by name is when a method has many parameters (especially
if many of those are optional), and, if a method has 17 parameters,
surely some day it will have 18. And users will expect that adding a
new parameter with a default is compatible. (Indy provides a nice
mechanism for sewing up this gap, but, we need to outline the
requirements and the compatibility guarantees.)
On 11/30/2017 3:56 PM, Guy Steele wrote:
Thanks, Remi, this is an excellent start! And it may be where we want to end
up.
But when the time comes that we dig into a serious discussion of putting named
method parameters into Java, I would like to see a broader exploration of the
design space before we settle on a specific design.
I’ve seen a lot of other designs for a lot of other languages, each with pros
and cons.
There are at least four more-or-less orthogonal properties a method parameter
can have:
(1) May it be specified by name (rather than by position) at the call site?
If so, can it be specified _either_ by name or by position?
(2) May the corresponding actual argument be omitted at the call site?
If so, what happens?
System-specified default value (such as zero or null) is
supplied.
Programmer-specified default value is supplied.
Value is a compile-time constant.
Value is recomputed at call time.
Can this computation depend on other argument
values (such as those to the left)?
No value is supplied.
A separate mechanism allows inquiry as to whether an
actual argument was provided.
The parameter type is actually an option type (choice;
if no value is supplied, you get an empty value).
(3) May the corresponding actual argument be duplicated at the call site?
(This may make little sense for Java, but is used extensively in Common
Lisp, where name-value lists may be built dynamically and then fed to `apply`;
allowing duplications makes it easy to override a default by just sticking a
new name-value pair onto the front of a list.)
(4) May the actual arguments be permuted at the call site—that is, appear in an
order other than the order in which they are declared in the method declaration?
(Typically the answer is “no” for positional parameters, but may be
“yes” or “no” for named parameters.)
(If a call contains both positional and named arguments, one can ask
whether the named arguments may be mixed in among the positional ones [yech!]
or must be kept separate, such as always appearing to the right of the
positional arguments.)
For each of the preceding four questions, there are these meta-questions:
Is the answer to the question the same for all parameters whatsoever?
Is the answer to the question the same for all parameters of the same
kind (such as named or positional)?
Is the answer to the question the same for all parameters in a single
method declaration?
Is the answer to the question the same for all parameters of the same
kind (such as named or positional) in a single method declaration?
In addition, there is the question of exactly what combinations of positional,
optional positional, named, and/or optional named parameters may be used within
a single method declaration.
And there is the question of what combinations of combinations may appear
within an overload set.
Many of these questions are in fact answered by specific choices in the
proposal below. I’m just looking to seeing (eventually) a thorough discussion
of the rationale for each choice. I provide this list of questions as one
possible starting point for that discussion.
—Guy
On Nov 30, 2017, at 3:21 PM, Remi Forax <fo...@univ-mlv.fr> wrote:
My note on named parameters:
Supporting real named parameters that works with overriding and backward
compatibility is hard, but i believe there is a sweet spot.
- a method can declare to support named parameters by using a new modifier
'named'.
so - all parameters are named or none are, you can not mix positional and
named parameters like in Ruby.
- a method is either support positional parameters or named parameters but
not both at the same time.
if there are several overloads, you can only have one named overload with the
same number of parameters,
which means that counting the number of parameters is enough to know which
method can be called.
a varargs method can not be named.
- when overriding a method, a named method can not override a non named method.
overriding a named method with a non named method is allowed to ease the
transition but emit a warning.
a named method that overrides another named method need to have the same
parameters with the same name at the same position.
- at call site, a named method has to be called using the syntax "name:
argument" for each arguments
by example:
ThreadGroup main = ...
new ThreadGroup(parent: main, name: "my group");
if a named method is called with positional arguments, the compiler emit a
warning
- at compile time,
a declaration site, all parameter named are stored in the Parameter
attribute.
a callsite, the compile insert an invokedynamic to the
NamedParameterMetaFactory with a method handle ref on the declared named method
and the names of all arguments in the order of the call.
a runtime, the NamedParameterMetaFactory verifies that the number of named
argument is the same and are a permutation of the declared parameters that are
retrieved by cracking the method handle ref as a MethodHandleInfo and calling
getParameters on the corresponding Constructor/Method, once the permutation is
calculated, the NamedParameterMetaFactory returns a ConstantCallSite on the
constant method handle (the method handle ref) permuted using
MethodHandles.permuteArguments.
To summarize, this let us use named parameters if the API designer want to
allow that,
there is a path for upgrading a method that uses positional parameters to use
named parameters, but not in the other way (like varargs),
not supporting permutations between overridden methods make thing far simpler
that they are otherwise.
cheers,
Rémi
----- Mail original -----
De: "Brian Goetz" <brian.go...@oracle.com>
À: "amber-spec-experts" <amber-spec-experts@openjdk.java.net>
Envoyé: Jeudi 30 Novembre 2017 19:29:41
Objet: Reader mail bag
We've gotten two submissions to the amber-spec-comments list.
1. "Forcing a pattern match", at:
http://mail.openjdk.java.net/pipermail/amber-spec-comments/2017-November/000000.html
2. "Named parameters in data classes", at:
http://mail.openjdk.java.net/pipermail/amber-spec-comments/2017-November/000003.html
I think the first asks for an alternate form of the "matches" operator
which would fail with a CCE, rather than evaluating to false, if the
match fails. This would be essentially saying, "This should match; if
it doesn't, that's an error." I think that's what's being asked, but
then he talks about an "unnecessary instanceof check", which makes me
wonder whether this is really just about optimization.
To be clear, the instanceof check is neither expensive nor unnecessary.
(Though we can optimize these away when we know the static type of the
target; if x is a Foo, then "x matches Foo f" can be statically
strength-reduced to "x != null".)
Note that the "if member.getKind() == VARIABLE" is merely a manual
optimization; you could easily leave that out and just match against
VariableTree. What we'd rather focus on is how to get to:
switch (member) {
case BlockTree bt: ...
case VariableTree vt: ...
}
while allowing the pattern to capture the quicker pre-test (kind ==
BLOCK) and maintain the performance without making the user worry about
this. We have some ideas here, but I don't think this "forcing" idea
really offers a lot.
The second (named parameters) was a question I was expecting.
I agree that being able to invoke constructors and methods by name can
sometimes result in easier-to-read code, especially when some parameters
have a sensible default value. (This is also a more complicated feature
than anyone gives it credit for, so it's not the "gimme" it is often
assumed to be.)
However, data classes is not the place to cram in this feature; this
should be a feature that stands on its own, and applies to all classes,
data- or not. One of the design goals for data classes is that a data
class should be essentially a "macro" for a class you could write by
hand; this allows easy migration from existing classes to data classes
(if they meet the requirements) and from data classes to full classes if
they expand to no longer fit the data class profile. The more that
*uses* of data classes or their members are different from the
corresponding use of regular classes, the more difficult this migration
becomes. (This is not unlike the design mandate with default methods;
from the client perspective, they're just ordinary virtual methods, and
you can't tell whether the method was implemented directly or inherited
-- they're just methods.)
So, while named parameters are a reasonable feature to explore, trying
to staple them onto data classes would be a mistake. They are their own
feature. We're open to exploring it, but we've got our plate full for now.