The FCM closures proposal, with the JCA extension, consists of multiple parts. This blog outlines how those parts fit together.
FCM+JCA spec parts
The FCM+JCA spec, contains the following elements:
- Method literals, also constructor and field literals
- Method references, also constructor references
- Inner methods
- Method types, aka function types
- Control invocation, in the JCA extension
These five parts all fulfil different roles in the proposal. But what is often not understood is how feasible it would be to implement less than the whole specification.
Method references and literals
Reviewing response to the entire debate, it is clear to me that method references and/or method literals have generally widespread appeal. The ability to reference a method, constructor or field in a compile-time safe and refactorable way is a huge gain for a statically typed language like Java.
It should be noted that although Method References appear only in the FCM spec, they could be added to the BGGA or CICE spec without difficulty. Also, they are included in the closures JSR proposal.
There are three areas of contention with method references and literals.
Firstly, should both references and literals be supported, or just references. Or, looking at the question differently, should literals have a different syntax.
The problem here is that if a method reference and literal have the same syntax then it is unclear as to what the type of the expression is. The FCM prototype demonstrates, I believe, that this can be solved using the same syntax. The approach taken is to say that the default type is a method literal, but that it can be converted (boxed) to a method reference at construction time. Any ambiguity is an error.
Method m = Integer#valueOf(int); IntToInteger i = Integer#valueOf(int);
In this example, IntToInteger
is a single method interface.
Because the expression Integer#valueOf(int)
is assigned to a single method
interface, the conversion occurs (generating a wrapping inner class).
The second issue is what a method reference can be boxed into. This is essentially a question of whether function types should be supported, and I'll cover that below.
The third issue is syntax, specifically the use of the #. Personally, I find this syntax natural and obvious to read, but I know others differ. I think it is important to get the syntax right, but final decisions on that can come later.
So, are method literals and reference required when implementing FCM? I would say 'yes'. These are simple, popular, constructs that naturally extend Java in a non-threatening way. Some have suggested omitting the literals as reflection is not type-safe, however this misses the point of the large number of existing frameworks and APIs that accept reflection Method as an input parameter.
Inner methods / Closures
ActionListener lnr = #(ActionEvent ev) { ... };
This is where the key difference with BGGA lies, notably over the meaning of return and the value, and safety, of non-local returns.
Opinions on this appear to me to be impacted by the generics implementation, where the decision was made to do what feels like 'half a job'. As a result, there is a meme that runs 'we must implement closures fully or not at all'. This meme is extremely unfortunate, as it is not allowing a rational analysis of the semantics of the proposals. Anyone supporting BGGA really needs to consider the mistakes that developers will make again and again with the non-local return/last-line-no-semicolon approach.
So, are inner methods required when implementing FCM? I would say 'effectively, yes'. Although you could just implement method literals and references alone, there are even bigger gains to be had from adding inner methods. They greatly simplify the declaration of single method inner classes, and allow much of the impact of closures in the style of Java.
Function types / Method types
#(int(String) throws IOException)
These allow a new powerful form of programming where common pieces of code can be easily abstracted. They simply act as types, but they have two different properties from other types.
Firstly, they have no name. This means an absence of Javadoc, including any semantic requirements of the API, such as thread-safety or null/not-null.
Secondly, they only describe the input and output types. This is a higher abstraction than Java has previously used, and will require a mindset shift for those using them.
So, are function types required when implementing FCM? I would say 'no, not required'. It is perfectly possibly and reasonable to implement FCM without method types. In fact, that is what the prototype does. In practice, this just means that all conversions from method references and inner methods must be to single method interfaces rather than method types.
Omitting method types greatly simplifies the conceptual weight of the change. The downside is that true higher order functional programming becomes near impossible. That may be no bad thing. Java is not, and never has been, a functional programming language. It seems very odd to try and push it in that direction at this point in its life.
A better alternative would be to pursue supporting primitive types in generics. This would greatly reduce the overhead of single method interfaces required by something like the fork-join framework.
Similarly, making single method interfaces easier to write (lightweight interfaces) would be a direction to take in the absence of method types.
Control invocation
withLock(lock) { ... } public void withLock(#(void()) block : Lock lock) { ... }
Control invocation forms are perhaps the only way forward in Java longer term because they allow us to escape from many of these language change debates. They allow anyone to write methods that can be used in the style of control statements. It is vital to remember that they are just methods however.
BGGA appears to build much of its spec around control invocation, and the non-local returns make perfect sense in this area.
The JCA spec defines that the calling code should be identical to BGGA, but the method invoked should be written differently. The aim of JCA is to provide an element of discouragement from using control invocation. This is because of the additional complexities in getting the code right (exception transparancy, completion transparancy, non-local returns etc). A different, special, syntax encourages this feature to be restricted to senior developers, or heavily code reviewed.
So, is control invocation required when implementing FCM+JCA? I would say 'no'. It is perfectly possibly and reasonable to implement FCM+JCA without control invocation (although of course that means it would just be FCM!).
The inclusion or omission of method types is also linked to control invocation, as method types are a pre-requisite for control invocation in the JCA spec.
Summary of possible implementations
Thus, here are the possible FCM+JCA implementation combinations that make sense to me:
- Literals and References
- Literals, References and Inner methods
- Literals, References, Inner methods and Method types
- Literals, References, Inner methods, Method types and Control invocation
My preferred options are number 2 and number 4.
Why? Because, I believe inner methods are too useful to omit, and I believe method types are generally too complex unless you really need them. (Also Java isn't a functional programming language.)
The key point of this blog is to emphasise that FCM is not a take it or leave it proposal. There are different options and levels within it that could be adopted.
This extends to versions of Java. For example, it would be feasible to implement option 1, literals and references in Java 7, whilst adding inner methods and maybe more in Java 8.
Summary
I've shown how FCM has parts which can be considered separately to a degree. I've also indicated which combinations make sense to me.
Which combinations make sense to you?