PRE-PROPOSAL: Named method parameters with defaults.

Reinier Zwitserloot reinier at zwitserloot.com
Sat Mar 21 15:21:35 PDT 2009


I think without having a way to specify default values, this really  
isn't going to replace the builder pattern. Being allowed to re-order  
should also be on the table, a the very least.

Here's all the technical detail of a proposal that might, maybe, fit  
inside project coin's scope.

First off:

Of all methods that have the same name (but different paramlists, e.g.  
overloading), only one is allowed to be named in the first place. If  
you're overriding it and also want YOUR version to be named, then your  
parameter list must be in the exact same order *AND* each parameter  
must have the exact same name as the overridden method (that's a new  
compiler check). If you're overridding a non-named method and you turn  
it into a named parameter method, that's fine. If you're overriding a  
named method and you don't make it named, that's fine too - but, it is  
still considered named. (100% analogous to how you can de-varargs a  
method in a subtype, but the compiler still considers it varargs).

That should solve overloading and inheritance problems.

Then, the syntax itself:

public named void foo(int x = 5, List<String> list = new ArrayList(),  
String foo) {
     //as normal
}

The 'named' is a new context sensitive keyword (if 'module' can be  
one, then this isn't too much of a stretch), and, only for named  
methods, you can supply expressions that serve as defaults. Each  
expression is evaluated exactly once, during your class initialization  
(so, the ArrayList above is SHARED amongst all method invocations! -  
I'd force it to be immutable but java does not have such a facility).

The system works by translating all value expressions into public  
final fields with a specific name. Javac, when finding a caller to a  
named method, will re-order as needed and add references to those  
fields to fill in the gaps. So, the above becomes:

public final int foo$x = 5;
public final List<String> foo$list = new ArrayList();

public void foo(int x, List<String> list, String foo) {
   //as normal
}

The fields have the Synthetic flag set. The method is compiled with  
param names, exactly the same way names are saved when turning on  
debug information (regardless of the actual -g:vars flag value), and a  
new flag called 'Named'.


The following call:

ClassName.foo(foo: "hello", x:10);

is compiled exactly the same way the following would be compiled:

ClassName.foo(10, ClassName.foo$list, "hello");

If you use the named syntax, then every parameter MUST be named. If  
you use no names, that's fine, but you don't get any of the sugar -  
You'd have to supply all 3 parameters, in the right order.



I'm fairly sure this is migration and source compatible, even in the  
case of subclasses, is not ambiguous (even in light of overriding  
classes, implementing types, and overloading the method), introduces  
no high-impact new features, other than forcing -g:vars (which I think  
is minor; most compilers already do this, especially for libraries, so  
that auto-complete can offer nicer names for the parameters than  
'arg0', 'arg1', and 'arg2'). Having the compiler dig through a type's  
entire parent tree including interfaces to figure out if the method is  
named in any of the subtypes seems high impact, but as I mentioned:  
It's not - javac already does this to figure out if an array in the  
last position is declared as a varargs in any supertype, and if so,  
treats that as a vararg.

Specifically, any library out there can update their classes and even  
their interfaces with 'named' keywords WITHOUT breaking any kind of  
compatibility; existing class files will continue to work, and  
recompiling old code in a javac v7 without accounting for the 'named'  
feature does not break those either.


To highlight the way the 'named' property is inherited from any  
supertype with an example:

public interface SuperType {
     void foo(String... args);
     named void bar(String arg);
}

public class Baz implements SuperType {
     void foo(String[] args);
     void bar(String arg);
}

Baz b = new Baz();
b.foo("a", "b"); //legal TODAY
b.bar(arg: "foo"); //would be legal too


Even if the subclass does not have -g:vars on, the names are still  
available via the supertype. In fact, without the 'named' modifier,  
the names of the parameters in a subclass are IGNORED (they could be  
different, and that would hence not be backwards compatible, which is  
bad). So, any 'named' marked methods must have equal names, but only  
one of the methods needs to be marked as such, and it 'infects' all  
subtypes. The syntax will not clash much with the parser, and the  
context sensitive keyword is extremely unlikely to introduce problems  
(if you have a *type* named 'named', you'd break things, but types are  
usually capitalized. The 'module' keyword suffers from the same issue,  
I believe.


Three questions:

A) Is this a good idea? Do you like it? Would you use it? (I think its  
stellar, but then, I'm proposing it).

B) Did I miss something (is it more complicated than I make it out to  
be), or is something unclear? Specifically, did I miss something that  
does not make it backwards, migration, and source compatible?

C) Might this be within scope for project coin? I'll definitely write  
it up if it stands a chance.


  --Reinier Zwitserloot



On Mar 21, 2009, at 18:56, Marek Kozieł wrote:

> Builders are really great tool, but they need to be written manually.
>
> In other way there will be problem, if final object need contains data
> and builder only key for that data.
>
>
>
> -- 
> Pozdrowionka. / Regards.
> Lasu aka Marek Kozieł
>
> http://lasu2string.blogspot.com/
>




More information about the coin-dev mailing list