Named parameters [was: Re: Reader mail bag]
Brian Goetz
brian.goetz at oracle.com
Thu Nov 30 21:41:53 UTC 2017
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 <forax at 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.goetz at oracle.com>
>>> À: "amber-spec-experts" <amber-spec-experts at 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.
More information about the amber-spec-experts
mailing list