The current vote forces us all to ask what proposal is best for the future of Java.
Personally, I don't find the BGGA proposal persuasive (in its current form).
But exactly why is that?
Touchstone debate
The closures debate is really a touchstone for two other, much broader, debates.
The first "what is the vision for Java".
There has been no single guiding vision for Java through its life,
unlike other languages:
In the C# space, we have Anders. He clearly "owns" language, and acts as the benevolent dictator.
Nothing goes into "his" language without his explicit and expressed OK.
Other languages have similar personages in similar roles.
Python has Guido. Perl has Larry. C++ has Bjarne. Ruby has Matz.
The impact of this lack of guiding vision is a sense that Java could be changed in any way.
We really need some rules and a guiding Java architect.
The second touchstone debate is "can any language changes now be successfully implemented in Java".
This is generally a reference to generics, where erasure and wildcards have often produced havoc.
In the votes at Javapolis,
'improving generics' got the highest vote by far.
The result of implementing generics with erasure and wildcards has been a loss of confidence in Java language change.
Many now oppose any and all change, however simple and isolated.
This is unfortunate, and we must ensure that the next batch of language changes work without issues.
Despite this broader debates that surround closures, we must focus on the merits of the individual proposals.
Evaluating BGGA
BGGA would be a very powerful addition to Java.
It contains features that I, as a senior developer, could make great use of should I choose to.
It is also a well written and thought out proposal, and is the only proposal tackling some key areas,
such as exception transparancy, in detail.
However, my basic issue with BGGA is that the resulting proposal doesn't feel like Java to me.
Unfortunately, 'feel' is highly subjective, so perhaps we can rationalise this a little more.
Evaluating BGGA - Statements and Expressions
Firstly, BGGA introduces a completely new block exit strategy to Java -
last-line-no-semicolon expressions.
BGGA uses them for good reasons, but I believe that these are very alien to Java.
So where do they come from? Well consider this piece of Java code, and its equivalent in Scala:
// Java
String str = null;
if (someBooleanMethod()) {
str = "TRUE";
} else {
str = "FALSE";
}
// Scala
val str = if (someBooleanMethod()) {
"TRUE"
} else {
"FALSE"
}
There are two points to note.
The first is that Scala does not use semicolons at the end of line.
The second, and more important point is that the last-line-expression from either the if
or
the else
clause is assigned to the variable str
.
The fundamental language level difference going on here is that if
in Java is a statement with no return value.
In Scala, if
is an expression, and the result from the blocks can be assigned to a variable if desired.
More generally, we can say that Java is formed from statements and expressions, while in Scala everything can be an expression.
The result of this difference, is that in Scala it is perfectly natural for a closure to use the concept
of last-line-no-semicolon to return from a closure block because that is the standard, basic language idiom.
All blocks in Scala have the concept of last-line-no-semicolon expression return.
This is not the idiom in Java.
Java has statements and expressions as two different program elements.
In my opinion, BGGA tries to force an expression only style into the middle of Java.
The result is in a horrible mixture of styles.
Evaluating BGGA - Non local returns
A key reason for BGGA using an alternate block exit mechanism is to meet
Tennant's Correspondance Principle.
The choices made allow BGGA to continue using return
to mean 'return from the enclosing method' whilst within a closure.
There is a problem with using return in this way however.
Like all the proposals, you can take a BGGA closure, assign it to a variable and store it for later use.
But, if that closure contains a return statement, it can only be successfully invoked while the enclosing method still exists on the stack.
public class Example {
ActionListener buttonPressed = null;
public void init() {
buttonPressed = {ActionEvent ev =>
callDatabase();
if (isWeekend() {
queueRequest();
return;
}
processRequest();
};
}
}
In this example, the init()
method will be called at program startup and create the listener.
The listener will then be called later when a button is pressed.
The processing will call the database, check if it is weekend and then queue the request.
It will then try to return from the init()
method.
This obviously can't happen, as the init()
method is long since completed.
The result is a NonLocalTransferException
- an unusual exception which tries to indicate to the
developer that they made a coding error.
But this is a runtime exception.
It is entirely possible that this code could get into production like this.
We really need to ask ourselves if we want to change the Java programming language such that we introduce
new runtime exceptions that can be easily coded by accident.
As it happens, the BGGA specification includes a mechanism to avoid this, allowing the problem to be caught at compile time.
Their solution is to add a marker interface RestrictedFunction
to those interfaces where this might be a problem.
ActionListener
would be one such interface (hence the example above would not compile - other examples would though).
This is a horrible solution however, and doesn't really work.
Firstly, the name RestrictedFunction
and it being a marker interface are two design smells.
Secondly, this actually prevents the caller from using ActionListener
with return
if they actually want to do so (within the same enclosing method).
The one final clue about non local returns is the difficulty in implementing them.
They have to be implemented using the underlying exception mechanism.
While this won't have any performance implications, and won't be visible to developers, it is another measure of the complexity of the problem.
In my opinion, allowing non local returns in this way will cause nothing but trouble in Java.
Developers are human, and will easily make the mistake of using return
when they shouldn't.
The compiler will catch some cases, with RestrictedFunction
, but not others, which will
act as a further level of confusion.
Evaluating BGGA - Functional programming
Java has always laid claim to be an OO language.
In many ways, this has often been a dubious claim, especially with static
methods and fields.
Java has never been thought of as a functional programming langauge however.
BGGA introduces function types as a key component.
These introduce a considerable extra level of complexity in the type system.
A key point is that at the lowest compiler level, function types are no different to ordinary, one method, interfaces.
However, this similarity is misleading, as at the developer level they are completely different.
They look nothing like normal interfaces, which have a name and javadoc, and they are dynamically generated.
Moreover, function types can be used to build up complex higher-order function methods.
These might take in a function type parameter or two, often using generics, and perhaps returning another function type.
This can lead to some very complicated method signatures and code.
public <T, U> {T => U} converter({=> T} a, {=> U} b, {T => U} c) {
return {T t => a.invoke().equals(t) ? b.invoke() : c.invoke(t)};
}
It shoud be noted that FCM also includes function types, which are named method types.
However, both Stefan and myself struggle with the usability of them, and they may be removed from
a future version of FCM. It is possible to have FCM inner methods, method references and method literals
without the need for method types.
Function types have their uses, however I am yet to be entirely convinced that the complexity is justified in Java.
Certainly, they look very alien to Java today, and they enable code which is decidedly hard to read.
Evaluating BGGA - Syntax
The syntax is the least important reason for disliking BGGA, as it can be altered.
Nevertheless, I should show an example of hard to read syntax.
int y = 6;
{int => boolean} a = {int x => x <= y};
{int => boolean} b = {int x => x >= y};
boolean c = a.invoke(3) && b.invoke(7);
Following =>
, <=
and >=
can get very confusing.
It is also a syntax structure for parameters that is alien to Java.
Summary
As co-author of the FCM proposal it is only natural that I should want to take issue with aspects of competitor proposals.
However, I do so for one reason, and that is to ensure that the changes included in Java 7 really are the right ones.
Personally, I believe that BGGA attempts something - full closures - that simply isn't valid or appropriate for Java.
It remains my view that the FCM proposal, with the optional JCA extension, provides a
similar feature set
but in a more natural, safe, Java style. If you've read the proposals and are convinced, then please
vote for FCM at java.net.
Opinions welcome as always :-)
References
For reference, here are the links to all the proposals if you want to read more:
- BGGA - full closures for Java
- CICE - simplified inner classes (with the related ARM proposal)
- FCM - first class methods (with the related JCA proposal)