At JavaOne on Monday I spoke on the topic of "The Next Big JVM Language". My conclusion wasn't what I expected it to be when I started researching the topic. Read on to find my conclusion...
The Next Big JVM Language
I then looked at what Java got right and wrong. Certainly Java got a lot right - its used by 10 million developers. But it got a lot wrong too...
Well, thats unfair! Java is 15 years old. We are applying a historical judgement to Java, and many of the choices made in Java 1.0 were appropriate then, but not now. Its much better to ask "what we have learnt from Java".
Learning from Java
I looked at some key points in the Java language that we have learnt over 15 years.
1) Checked exceptions. Spring rejects them. Hibernate rejects them. Java EE rejects them. They are a failed experiment (good in theory, bad in practice). Now, many reading this blog still hold checked exceptions dear to your hearts. But I'm afraid its finally time to say "wake up and smell the coffee". Checked exceptions have been rejected by all the key industry API writers and leaders at this point. If you're still using or advocating checked exceptions, then I'm afraid your skill set is 5 to 10 years out of date. Period. (I know that may sound harsh, but to those in that camp, you seriously need to open your mind to what modern API design is about.)
2) Primitives and Arrays. Both these features expose low level details from bytecode. They break the "everything is an object" model. The lack of generics over primitives is a classic example of this. The correct solution is a language where the source code does not have exposed primitives or arrays, and the compiler (or perhaps the JVM) works out if it can optimise it for you.
3) Everything is a monitor. In Java and the JVM, every object is a monitor, meaning that you can synchronize on any object. This is incredibly wasteful at the JVM level. Senior JVM guys have indicated large percentage improvements in JVM space and performance if we removed the requirement that every object can be synchronized on. (Instead, you would have specific classes like Java 5 Lock)
4) Static. Code in static methods is inherently less reusable and accessible than code in objects. This, together with constructors, often results in a need to have explicit factory interfaces, making APIs more complex. A better solution is singleton objects, which can be used most of the time just like a static, but can be passed as an object when required.
5) Method overloading. One of the most constraining parts of the Java language specification is method resolution, where the compiler has to work out what method you intended to call. Resolution touches superclasses, interfaces, varargs, generics, boxing and primitives. It is a very complex algorithm, that is difficult to extend. Having no method overloading, except by default parameters, would be a huge win.
6) Generics. Java generics is complex, especially around the ? extends and ? super variance clauses. Its too easy to get these wrong. The lesson from Java should be that use-site variance hasn't worked out well.
I could have chosen others, but these are a selection of items where we have learnt from Java.
What might NBJL contain?
Looking forward, I argued that human factors are key in the adoption of a next mass-appeal language. I put forward a simple test for the ability of any new language to be adopted:
1) Take a piece of code in the new language. A piece of reasonable complexity that a typical developer would be expected to deal with day-to-day.
2) Give the code to a mid-level developer. Someone who is not interested in blogging, tweeting or new languages in general.
3) Can they make a reasonable guess as to what the code in the new language does? Without any help or training.
Now this is a fairly harsh definition of how far NBJL can evolve, but it is I believe quite a practical one. The truth is that we need to be able to transition to the new language without massive training programmes.
In terms of features, I covered a long list of features and issues that a new language should address.
- C-like syntax (familiar and good-enough)
- Static typing (dynamic is too loose and ineffective for this type of language)
- OOP with functional elements (pure functional too hard for mainstream)
- Easy access reflection (to escape the static typing restrictions)
- Properties (because getters and setters are crazy)
- Closures (capturing looping design patterns)
- Null-handling (preferably a means to declare whether each variable can or cannot hold null)
- Concurrency story (something better than raw threads and shared mutable state)
- Modules (need to be thinking in terms of larger units)
- Tools (need a language to be designed to help tool writers)
- Extensibility (allowing some additions without going back to the language designer)
There are many other concepts that could be discussed - language design is fairly obviously a design artifact, and so different views and opinions are likely.
So, what language?
Clojure is a functional programming language, using syntax from the LISP family.
It has some great ideas on handling state, which change completely the approach Java developers
are used. to.
However, it is a million miles away from Java in syntax and function approach.
NBJL isn't Clojure.
Groovy is a dynamic language with optional typing.
It is heavily based on Java, using the syntax and structures directly in many cases.
This makes it very quick and easy to pick up, and use.
Its strengths are in scripting and web pages, where the dynamic and meta-programming elements shine.
The dynamic nature makes it a poor choice for large bodies of core entterprise server logic.
NBJL isn't Groovy.
Scala is a OO/functional language, using C-like syntax.
On deeper examination, it can be seen that the functional side is more significant.
In particular, the culture of the language pushes users to the more pure functional solutions.
Scala is statically typed, to the extent that the generics of Scala is apparently Turing complete.
But, does writing a programming language in the generics of another programming language really make sense?!!
To cover all the elements of Scala complexity really needs to write a separate blog post.
Suffice to say that Scala simply gives developers way too much rope to be able to hang themselves by.
While it may at first glance appear to offer the better than Java features that are being searched for,
it quickly bites your head off once you go beyond the basics - its simply a language too complex
for the mainstream.
NBJL isn't Scala.
Fantom is an OO language with functional elements, using C-like syntax.
It has very simple and neat solutions to topics as diverse as nullable types, immutability
It has a static type system with a relaxed approach.
Geerics are only supported on Lists, Maps and Functions, and developers cannot add their own.
To compenstate, the language sutomatically adds a cast wherever a developer would normally
have needed to add one.
While Fantom contains almost a complete sweep of what a sensible mainstream language should contain,
it hasn't received that much attention. One point of concern is whether the type system is
strong enough to attract lots of developers.
Fantom is closest to NBJL of these languages, but seems unlikely to succeed as the more relaxed type system seems to scare people off.
At a personal level, each of these four languages will teach you something new if you learn it. Clojure and Scala in particular will teach you about functional programming if you've never been exposed to it. However, NBJL is about picking a language suitable for use by everyone for all tasks, in a team and enterprise environment. While each of these four has a niche, none of them are currently placed to jump up and replace Java.
An alternate approach
There are 10 million Java developers. Any improvement that affects all 10 million has a big benefit for the cost. But none of the current languages is capable of being that next language.
Maybe we should reconsider Java?
What if we created a backwards incompatible version of Java (the language)?
Where we fixed the things we know are broken? (exposed primitives, arrays, checked exceptions, ...
Where the changes were not too massive to create the need for formal training courses?
Where a tool converts old code to new code (and vice versa) with 100% accuracy? (providing the essential backwards compatibility)
Where features like closures and properties could be added with the current compromises?
Where you only compile modules, and never single class files?
Where the compiler/module-linker can determine that two versions of a module use the same bytecode for the operations you actually call, therefore they are actually fully compatible? (and other similar module version transformations)
What if the community asked Oracle to do this instead of JDK 8? (accepting a delay to 2013)
Or as JDK 9?
Is it time to learn the lessons of Java? And apply those lessons to Java itself?
Any talk on languages is controversial, as each language has a specific world view and fans. I rejected Clojure, Groovy and Scala as NBJL even though each is a good language in its own way. I concluded that Fantom is closest to the statically typed mainstream language that is needed, yet its simple type system and some of its APIs are counting against it.
As a result, my conclusion is that the language best placed to be the Next Big JVM Language is Java itself - just a backwards incompatible version. And I suggested that maybe we should accept a 2013 release date (instead of 2012) for JDK 8 as a backwards incompatible version.
Feedback expected! Please use #bijava to talk about backwards incompatible Java on twitter.
PS. I'll get comments invalidly marked as spam out as soon as I can!