Java Shark Jump Redux
My last post on the iffy implementation of FP in Java 8 generated a lot of interest. Many thanks for all the comments received. I would like to clarify two things, firstly why Java 8 FP worries me and secondly an alternative way of solving the problem I posed.
Perl OO – A Warning From History
Those of you who are (like me) rich in years will remember Perl, the scripting language that reined supreme in the 1990’s and early 2000’s. I have a huge fondness for Perl, which I have to admit is not shared by the majority of my colleagues :-)
A turning point in the history of Perl came when it was obvious that the industry had embraced OO and the language would suffer badly if it did not do the same. So it was decided to adopt OO in the most minimal way possible, by adding a single function to the language called bless.
A call to bless took an arbitrary data structure and associated it with all the functions in the containing package. In effect it converted a Perl package into a class and a blessed table (or other structure) into an object. If you were accomplished in both Perl and OO then you could admire the cleverness of this slight of hand. If not then you were forever befuddled. When asked to teach OO on a Perl course I taught the delegates OO in Ruby first and then tried to explain how Perl achieved the same effect. I cannot claim I was always successful.
The decision to partially adopt OO via bless was (I believe) the worst of all possible worlds. The language cognoscenti could sit back in the knowledge that they had given the OO proletariat what they needed, without compromising their original vision. The proletariat however got tired of trying to understand this conjuring trick (amongst others) and moved en masse to Ruby and Python.
To me there is a clear comparison between OO in Perl and FP in Java. Its not enough to say “yes its now possible to program in an FP way in Java” – we need better than possible. We need Java to embrace and enable the FP style of programming, not just accommodate it.
Solving My Problem Via Reduce
In that regard lets look at another way of solving the problem I posed, namely by using reduce. As noted in the last post flatMap is the correct tool for the job, but when faced with an intractable problem functional programmers instinctively fall back on reduce. A first attempt would look like this:
When used this way reduce operates like a while loop (or recursive function) with a collecting parameter. Of course given that we are after an array of characters there is a bit more work to do:
This is not ideal, but it gets the job done without the need to write any custom conversion functions. There is however one small issue – it wont compile.
The compiler message isn’t a lot of help and neither is your knowledge of reduce in other languages (mine didn’t anyway). Even worse the Javadoc is unhelpful (to put it mildly) on the subject:
However it appears we are missing a final lambda. Lets put in a fake one just to see what happens:
This compiles and prints the correct answer:
All would be great were it not for the niggling doubt about WTF that final parameter is doing. Heres the complete program so far:
After deep reflection (i.e. asking on StackOverflow) it turns out that this lambda is only used if you are running the operations in parallel, in which case the compiler needs to be told how to combine intermediate results together. So if we insert a call to parallel above we get a NullPointerException:
We can have some fun and modify the lambda as below:
As you might expect this gives the output w i b b l e, but only when the call to parallel is present. Heres an expanded version to illustrate what is going on with the threading:
Now that we are running operations in parallel our collecting parameter cant do any collecting anymore, so I’ve switched to using strings where the initial value is “-“. Heres the output on my machine:
As you can see we seem to be allocating the strings from the array to multiple threads, appending the initial value to members on each thread and then combining the results.
If you thought that the solution to my earlier problem was to go with reduce rather than flatMap then your FP instincts were good, but yet again we are let down by Java 8. The version that would work in other languages fails in Java 8 because it requires an extra parameter that would only be used if we chose (incorrectly) to add parallelism later on. To put it another way the simplest thing that will shut the compiler up will not cause us any problems unless we choose to introduce parallelism later. This is not the seamless concurrency we were hoping for. So I stand by my original thesis that Java 8 is a bridge too far and more complex than Scala et al…