 |
Call me Santa
Posted by forax on December 17, 2006 at 07:11 AM | Comments (22)
Since my last
post, i've played with javac
enought to be able to provide a patch that enables
to let the compiler infers the type of local variables.
Life is a matter of choices
If you have already read the last
Peter Ahé's blog entry
you know that, at least, two proposed syntaxes compete.
The first one suggested by James Gosling and named 'Algol' in the
prototype use ':=' instead of '=' to declare local variables.
I've tweak the example widely used
to explain generics and autoboxing without declaring the type
of the local variables.
// print a frequency table of words
public static void main(String[] args) {
map:=new HashMap<String,Integer>();
for(word:args) {
freq:=map.get(word);
map.put(word,(freq==null)?1:freq+1);
}
System.out.println(map);
}
Note that the compiler infers local variable in foreach loop too.
The second syntax supported by Peter Ahé and
Christian Plesner Hansen
use the keyword final to acheive the same goal.
My running example with the 'final' syntax:
public static void main(String[] args) {
final map=new HashMap<String,Integer>();
for(final word:args) {
final freq=map.get(word);
map.put(word,(freq==null)?1:freq+1);
}
System.out.println(map);
}
How to use the prototype ?
To enable the Algol syntax, call javac in this way:
java -jar prototype-1.7-b04.jar -XDinferLocally=Algol TestAlgol.java
or if you prefer the final syntax:
java -jar prototype-1.7-b04.jar -XDinferLocally=final TestFinal.java
What you can do ?
You can test the prototype and let us know what you thinking.
Do you prefer the Algol syntax, the final syntax or none of both
(perhaps because you hate the idea behind) ?
Download the prototype :
prototype-1.7-b04.jar
Download the prototype patch against the
Open
JDK Compiler (1.7b04): patch-1.7b04
Cheers, Rémi
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
It seems a little self-defeating to have to use "final" to get the inferance.
final s = "String";
String s = "String";
vs
s := "String";
String s = "String'
In the first example Im saving 1 character, though Im getting final for free. Im not sold on the idea that because you can make something shorter it has to be final. I would expect
s := "String";
s = new Object();
to be a compiler error anyway.
s := "String";
s = "String2";
What's evil about doing this?
thanks for the patch Santa,
leouser
Posted by: leouser on December 17, 2006 at 07:35 AM
-
Remi (sorry, not sure how to type your name correctly in this form):
Thanks, this is nice to have--glad I don't need to keep track of your patches myself.
Why, exactly, does the := also make the variable final? This seems non-intuitive, and worse, the operator is "light" enough that it won't be immediately clear from reading a block of code which locals are final and which are not. What is the reason, exactly? I don't like Peter's suggestion exactly for this reason--it seems to me these two (final, type inference) are serving two completely different goals.
Thanks
Patrick
Posted by: pdoubleya on December 17, 2006 at 08:16 AM
-
Ok, some more testing
s1 := "a string";
s1 = "another string";
throws an exception
java.lang.AssertionError: store unsupported: immediate(new string)
at com.sun.tools.javac.jvm.Items$Item.store(Items.java:210)
at com.sun.tools.javac.jvm.Items$AssignItem.drop(Items.java:683)
at com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1587)
at com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1092)
at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:677)
at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:712)
at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:698)
at com.sun.tools.javac.jvm.Gen.genStats(Gen.java:749)
at com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1002)
at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:757)
at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:677)
at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:712)
at com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:935)
at com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:871)
at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:657)
at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:677)
at com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2180)
at com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:634)
at com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1306)
at com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1276)
at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:782)
at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:747)
at com.sun.tools.javac.main.Main.compile(Main.java:383)
at com.sun.tools.javac.main.Main.compile(Main.java:309)
at com.sun.tools.javac.main.Main.compile(Main.java:300)
at com.sun.tools.javac.Main.compile(Main.java:86)
at com.sun.tools.javac.Main.main(Main.java:71)
but
s1 := "string";
s1 = new Object();
throws an incompatible types compiler error.
Patrick
Posted by: pdoubleya on December 17, 2006 at 08:29 AM
-
Looks as if it only makes sense for final variables. Not a problem inferring the type on the right hand side has.
I guess as an alternative to final you could use the sort of analysis that ?: and definite assignment use. Still, this whole approach of obfusticated declaration makes me wretch.
Posted by: tackline on December 17, 2006 at 10:58 AM
-
Patrick, it seems i fuck up, s1 is considered as a litteral, so
it can't be re-assigned.
Should be fixed now :)
Rémi
Posted by: forax on December 17, 2006 at 12:30 PM
-
Remi, I think Neal Gater have a valid point, saying that generics type should be inferred from the variable declaration and not the intializer/constructor. Simply because variable type itself will be used later and it is only makes sense to have its type declared explicitly. So, the following notation seem more natural to me:
Map map := new HashMap();
Moreover, it also would allow to use non-interface calls when, say Collection type is initialized with LinkedList.
Posted by: euxx on December 17, 2006 at 02:29 PM
-
Example in my comment should look like this:
Map<Pair<String, Integer>, Node> map := new HashMap();
Posted by: euxx on December 17, 2006 at 02:35 PM
-
Get yourself an IDE...
Posted by: tobega on December 17, 2006 at 02:41 PM
-
Eugene, i don't see why a local variable has to be declared using
an interface. In general, we use an interface when you want
to potentially use different implementations (or to hide
an implementation detail but that not the case here).
The syntax proposed by Neal don't seem to answer to
the same problem and in my opinion this problem is
less frequent.
Map<Pair<String, Integer>, Node> map;
if (blahblah)
map= new HashMap<>();
else
map = new TreeMap<>();
Rémi
Posted by: forax on December 17, 2006 at 03:27 PM
-
Even though this feature is not usefuly exclusively for final variables, since I'm a strong proponent of intense use of 'final' (see this article's subsection "The final modifier and refactoring: towards
functional-style Java"), I'm all in favor of that option :-)
Even today, we could argue that the 'final' keyword means not only that a variable's value is immutable, but as consequence, its concrete type is also immutable, so there is some connection to typing. (Unfortunately I think this information is lost after javac, but it could speed up the compiler's work with devirtualization - RFE for the classfile format?)
The ':=' operator is an odd choice because in languages that have that operator, it means assignment, so these languages can use '=' for equality. And it's a bigger change to the language: a new token / lexical construct, while the overloading 'final' only makes a very small change to the syntax (not requiring the type). Both proposals change the semantics too, obviously.
And yes, inference for generic declarations is an absolute must-have. People how tell me that this isn't a good idea, I think, didn't have to write really complex generic code, and people who say "use a good IDE" (I always use) don't care for code maintenance, and never heard about the DRY principle, so you can just google for that (DON'T REPEAT YOURSELF) (it has alternative names, like OOO - Once and Only Once, and SPOT - Single Point Of Truth).
Posted by: opinali on December 18, 2006 at 02:27 AM
-
opinali, your maintenance remark does not make sense at all. Do you mean that if you want to change from Integer to Double you will have one less place to do it? A place in which you have to change the right-hand-side anyway? Wow, talk about saving time...
Posted by: tobega on December 18, 2006 at 04:44 AM
-
DRY and OOO are good, but readability is another major point. In a real case - not the classic five lines for an example - 'map' could be a field declaration and the reader should go everytime back and forth to its declaration to understand which type is freq. Frankly speaking, I don't understand all this hysteria for saving a couple of characters (I'm not talking of this example, of course, but about the whole trend in the community). We're screwing up a language that has been powerful, elegant and simple for ten years. And project failures certainly aren't due to that bunch of characters more, but to lack of design and so on.
Granted - looks like the Java community has been plagued by Microsoft-style mania.
Posted by: fabriziogiudici on December 18, 2006 at 09:27 AM
-
Remi, more justifying example could be something like this:
Map<Pair<String, Integer>, Node> foo() {
return new HashMap<>();
}
PS: do you guys also have to manually escape angle brackets when writing comments or I am missing here?
Posted by: euxx on December 18, 2006 at 11:05 AM
-
This is one of those "It would have been cool if Java had been designed this way" features.
Adding it now is just silly..
Posted by: dog on December 18, 2006 at 11:08 AM
-
I disagree that the issue is in saving keystrokes; that's more of a side benefit, IDE or no IDE. I think the most convincing argument is that it reduces visual clutter, clutter which adds no information to the program you're reading.
As far as final variables go, all well and good for programs built around that concept, but the mass of Java code written until now was not--and that by free choice, as final was always there to be used to local variables. It's a separate concern and we should treat it like that. When Gosling blogged about this a few months ago, he suggested := for "infer type and assign", and :== for "infer type, assign, and make final". Not a bad idea to have two available.
Patrick
Posted by: pdoubleya on December 18, 2006 at 11:27 AM
-
the C# spec is published as a standard right? you can get the answers direct from that surely?:o)
Posted by: asjf on December 18, 2006 at 12:44 PM
-
Hi Rémi-
This is really, really cool. Thanks for doing this. This is something I've been wanting for a while, but don't have the skills or gumption to do myself.
Are you familiar with the Boo language? Here's Boo's home page and wikipedia entry. Its most appealing feature is Ruby-style type inference.
Boo has yet to support generics. It appears that your prototype does. (I haven't downloaded it yet.) Are there any outstanding/known issues on supporting both type inference and generics?
I am completely ambivalent about the 'final' keyword and the ':=' operator for local variables. What's the point? I like Boo's approach: less is more.
Boo has two other features that I like: eliminating the 'new' operator and eliminating the end-of-statement ';' semicolon.
Some of the other features from Boo, I can do without. In particular, 'duck typing', which swipes the 'var' declaration from Visual Basic to denote a variable that can have any type. (If I wanted dynamic typing, I'd use a dynamic language like JavaScript or Ruby.)
This is going to sound like a stupid question: Is type erasure (for generics) necessary? I don't yet understand type systems. But since you're 'forking' the Java language, do you have to keep type erasure? Best that I've been able to understand the issue, type erasure was deemed required for backwards compatibility.
Do you have any ideas for eliminating stuff (cruft) from the Java language? One example might be 'transient'.
Have you seen David Bacon's Kava programming language? It sports "lightweight objects" and extensible scalar types. As I understand it, Bacon's approach eliminates the need for boxing/autoboxing.
My idealized (fantasy) Java-style (procedural) language would have type inference (like your prototype) and lightweight objects. With JDK6's new escape analysis (and ongoing optimizations), this would pretty much be heaven.
After that, my wishlist would be syntax for reflection (vs the current API), clarity on finalization and guaranteed object initialization (which would involved the JVM). The "Nice" programming language allows you to declare that a variable cannot be 'null'. This sounds like a good idea, but I'd want to "try before I buy".
Again, thanks for doing this. It's really exciting. I'll be trying it out and reporting back.
Cheers, Jason
Posted by: josgood on December 27, 2006 at 05:22 PM
-
Hi Jason, i've played with Boo! last year and found that it rocks.
There is lot of gotcha when you want to support type inference and generics
but my prototype does nothing more than the java compiler already done
when it infers the return type of '?:'.
My point is not to develop a Java like, but to propose solutions to
enhance Java.
By example, eliminating the new keyword is not possible in Java
because Java have different scopes for types, variables and methods
and supports by example to have a method with the same name
that a constructor.
About duck typing, i think something is possible with the introduction
of the new bytecode invokedynamic.
Erasure is necessary until we have runtime support of
generics by the VM. If you provide such support, i can do something :)
I don't see a problem with transient, you don't like serialization ??
The idea behind escape analysis is to let the VM found what object is
lightweight or not, in order to not require the developer to do that.
I agree with you that a concise syntax for reflexion could be great
else the JSR 308
is about adding annotation like @NonNull.
regards,
Rémi
Posted by: forax on December 28, 2006 at 07:14 AM
-
Hi Rémi-
Thanks for the followup. It helped clarify a bunch of stuff.
What are the prospects runtime support for generics (in the JVM)? There's all sorts of activity (annotations, enums, closures). But I'd have to say that I'd vote for eliminating the use type erasure. Mostly because medium skill devs (like myself) simply don't understand what's going on.
Okay, if you can't whack 'new', how about the end-of-line semicolons ';'?
Do you know the use case (or motivation) for 'duck typing'? I've just never had the need or desire. I've done plenty of LISP and JavaScript as well as some Scheme. Languages with dynamic typing are great. But it's a different style (culture) of programming. I've never felt the need to mix-n-match programming styles (a la C++).
I'm not for or against 'transient'. I was just trying to come up with an example. People far more knowledgeable than me (on our user group listserv) says its origin is a hack to enable serialization and that it's no longer needed.
Consider me unenthusiastic about annotations. I've never understood the motivation for having them. Others (mostly Ted Neward) have tried to explain to me how wonderful they are. That said, I haven't seen a syntax alternative to '@NonNull' that's any better. So whaddya gonna do? (haha)
You already know this, but for clarity... Kava's definition of 'lightweight objects' eliminates the distinction between scalars and objects. Scalars behave like objects, in a SmallTalk-esque sort of way. Escape analysis's notion of 'lightweight' is allocating objects on the stack when able.
The combo of stack allocated objects and Kava's 'lightweight objects' would be boss (unstoppable). It'd enable all that Java Grande stuff (e.g. game programming, image processing, simulations, numerics).
Lastly, are you going to create a project page for this effort?
Cheers, Jason
Posted by: josgood on December 28, 2006 at 10:18 AM
-
The proposal on Neal Gafter's blog came from Javapolis, where I chatted to him about this issue. I proposed this syntax, which Neal seems to quite like:
Map<Pair<String, Integer>, Node> map = new HashMap<>();
The reasoning is that IMO, most Java programmers expect the LHS of a variable assignment to fully define the type that will then be worked with. Thus the integrity of the LHS needs to be preserved. The RHS is where any inference can happen. But I also wanted to have a clear syntax to indicate to developers that something was going on, rather than nothing. Hence I used <> for "infer generic types".
I still believe that this is the best way to address this issue in Java-style. And we must remember that using Java-style (not Ruby/Groovy/Haskell/Algol/...) is very, very important to the decisions we make to change the language.
Posted by: scolebourne on January 06, 2007 at 08:22 AM
-
I only realised that this was valid Java recently:
System.out.println(new Object(){int x=5;}.x);
Presumably this would be extended with this proposal:
final o=new Object()
{
int x=6;
String s="Hello";
};
o.x=7;
o.s="Ugh"
Posted by: ricky_clarkson on January 08, 2007 at 05:01 PM
-
I'm for Algol notation:
events := new HashMap()
First, we already overload ":" in for each loop, second it's less verbose and confusing then final (btw, why not default then ;), third it's still looks like an assignment
Valery
Posted by: vsilaev on January 09, 2007 at 01:28 AM
|