I would like to ask you two questions please.
1. Your table of contents lists recursion. I have had to maintain code in 370/Assembler that used recursion heavily. When there was a problem it was nearly impossible to figure out from which point the recursive code was called and therefore nearly impossible to follow the path of the code. Does recursion in functional programming in Java have any constraints to prevent that sort of situation? The functional code samples I've seen look very complicated and using them for recursion colde make for code that even if I wrote it I would not want to own it.
2. I have taught Java a lot and I always started out with describing Java as an object-oriented language and explaining what that means. Functional programming is decidedly not object-oriented, and therefore breaks the essential paradigm of the language, making it more like C++, a functional language plus objects. Doesn't this make Java lose the benefit of being a truly OO language as opposed to C++ or C# or something like that? If I taught a course on Java and got to the functional programming stuff, I'd have to tell students , "I know I said Java is OO, but with Java 8, you can forget that." What is your view?
Ken Duncan wrote:...like C++, a functional language plus objects.
I'm sure there are many who disagree with that characterization.
Addressing Ken's original points:
1. I'm not surprised that trying to debug recursive calls in assembler was hard. Lots of things are hard in assembler (and I've done plenty of that, including recursive programming). Recursion in Java has been a thing for decades -- it's not new to functional programming in Java and, if used responsibly, shouldn't be complicated in any language. If you have any problem where functional decomposition naturally leads to the description of a sub-problem as a "smaller" version of the original problem, then you have recursion. Recursion is a feature of the problem itself, not necessarily the solution. For example, tree-walking solutions can be iterative or recursive, even though a tree data structure is inherently recursive in nature. The recursive solutions for tree-walking should be substantially simpler than any iterative solutions.
2. Yes, Java is a mostly object-oriented language (it pales in comparison to some others -- and it is certainly not "a truly OO language") and, yes, functional programming is a very different paradigm. Java 8 didn't make Java non-OOP: it introduced a lot of new classes to support a somewhat functional style of programming, and it introduced some syntactic sugar to hide some OOP-ness when writing that somewhat functional code. Pierre-Yves has said in a couple of threads here that his book isn't about those Java 8 features but about a more general functional programming style in Java, built on introducing more appropriate abstractions.
Sean Corfield wrote:2. Yes, Java is a mostly object-oriented language (it pales in comparison to some others -- and it is certainly not "a truly OO language")
For a while, I found it curious that Jim Coplien always scoffed at the claim that Java is an Object-Oriented language. Jim will take any opportunity he gets to say that Java is, in fact, a class-oriented language. I've only recently realized what he meant by that. It does the job for me though, and it's kept me with a job for a while now and I'm not in the habit of pooping in my own bed, so... (although I am guilty of the occasional Dutch Oven, both metaphorically and literally)
I agree, Java 8 did not make Java non-OOP all of a sudden. And just because it now has features for the folks who clamor for something to ease their FP-envy in the JVM, doesn't necessarily make it an FP language either. It's still kind of caught in the same awkward place it's in with respect to OOP, only a little more awkward and even less elegantly so with respect to FP. I don't worry about pedigree too much though. As long as it helps me do my job better and produce apps that do better, I'm willing to feed and care for the little mutt and get what I can out of it.
Sean Corfield wrote:You might, more accurately, say "... C++, a multi-paradigm language, that supports object-oriented programming, with some functional programming features too".
That's exactly what someone in that Quora thread I cited earlier attributed to Bjarne Stroustrup as saying (paraphrasing).
I am not sure whether you refer to my implementation of stack safe recursion or to some other, and I don't know what you really mean by "which point the recursive code was called". The only cases where I encountered this problem was when using stack based recursion, where the stack trace was not long enough to show the origin of the call. This obviously does not happen with stack safe recursion, since it is implemented on the heap. If it were to happen, you would get an OOME rather that a stack overflow.
This said, we often read that the main advantage of recursion is that it allows to handle recursive problems in a very simple way, using code that exactly conforms to the problem description. This is in my opinion not always a good thing and it does not mean that it should be implemented recursively. In the book, I describe a very simple way to implement recursion in a stack safe manner. On the other hand, I do not promote using recursion everywhere. Functional Programming generally abstracts recursion into folds. This gives two benefits:
- it allows hiding the implementation, which can then be realized by any suitable means, even imperative ones.
- It makes new patterns to appear. I once wrote that most computations could be expressed as folds. For example, getting a value from an Optional is a fold. Of course, it has nothing to do with recursion. This is because recursion is often an implementation detail. Saying that the sum of a list is 0 if the list is empty and head + sum(tail) otherwise might seem elegant, but it is just the description of an implementation of a specific case. It is in my opinion much better to express it as a fold, which make clearly apparent that there are three elements: the identity, the function, and the fold operation. This is not obvious with the recursive description, and this is why recursion is sometimes called the "goto of functional programming".
In a more general way, I want to make it clear that what I expose in the book is not what programmers should do, but what they can do. Solving all problems explicitly using stack safe recursion in Java would certainly not be a good thing.
Regarding the problem of OOP vs FP, I don't think this is very important. It's not what the language is that is important, but what you do with it. I don't care if some aspects of OOP are not compatible with FP (which is yet to be proven). OOP and FP are tools. They are not incompatible by themselves, in the sense that you would not be able to, or perhaps should not, do some functional programming stuff with a language labelled "OOP" and conversely. I have yet to see a language that is exclusively OOP or exclusively FP. This simply do not exist, despite the efforts made by some to create it. So I would not say that FP breaks the essential paradigm of the language. It simply uses a different paradigm. It may even use part of the OOP paradigm + FP. In the same way, you can do OOP while using some elements of the FP paradigm. Clearly separating functions from effects or insuring referential transparency, does not break the OOP paradigm. Even passing functions to methods or returning functions from methods do not break this paradigm. In fact, this is something that has always been done in Java, and there are even named patterns for this.