Why Functional Programming in Java is Dangerous

In my day job I work with a lot of very smart developers who graduated from top university CS programs such as MIT, CMU, and Chicago. They cut their teeth on languages like Haskell, Scheme, and Lisp. They find functional programming to be a natural, intuitive, beautiful, and efficient style of programming. They’re only wrong about one of those.

The problem is that my colleagues and I are not writing code in Haskell, Scheme, Lisp, Clojure, Scala, or even Ruby or Python. We are writing code in Java, and in Java functional programming is dangerously inefficient. Every few months I find myself debugging a production problem that ultimately traces back to a misuse of functional ideas and algorithms in a language and more importantly a virtual machine that just wasn’t built for this style of programming.

Recently Bob Martin came up with a really good example that shows why. Here’s a bit of Clojure (a real functional language) that returns a list of the first 25 integers:

(take 25 (squares-of (integers)))

This code runs, and it runs reasonably quickly. The output is:

(1 4 9 16 25 36 49 64 … 576 625)

Now suppose we want to reproduce this in Java. If we write Java the way Gosling et al intended Java to be written, then the code is simple, fast, and obvious:

for (int i=1; i<=25; i++)
    System.out.println(i*i);
}

But now suppose we do it functionally! In particular suppose we naively reproduce the Clojure style above:

import java.util.ArrayList;
import java.util.List;

public class Take25 {

    public static void main(String[] args) {    
        for (Object o : take(25, squaresOf(integers()))) {
            System.out.println(o);
        }
    }
    
    public static List<?> take(int n, List<?> list) {
        return list.subList(0, n);
    }
    
    public static List<Integer> squaresOf(List<Integer> list) {
        List<Integer> result = new ArrayList<Integer>();
        for (Integer number : list) {
            result.add(number.intValue() * number.intValue());
        }
        return result;
    }
    
    public static List<Integer> integers() {
        List<Integer> result = new ArrayList<Integer>();
        for (int i = 1; i <= Integer.MAX_VALUE; i++) {
            result.add(i);
        }
        return result;
    }
    
}

Try to run that. Go ahead. I dare you....OK, recovered from the heap dump yet?

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:2760)
	at java.util.Arrays.copyOf(Arrays.java:2734)
	at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
	at java.util.ArrayList.add(ArrayList.java:351)
	at Take25.integers(Take25.java:30)
	at Take25.main(Take25.java:9)

How did Clojure handle a function that returns every single int, while Java crapped out? The answer is that Clojure, like pretty much all true functional languages (and unlike Java) does lazy evaluation. It doesn't compute values it doesn't use. And it can get away with this because Clojure, unlike Java, is really and truly functional. It can assume that variables aren't mutated, that the order of evaluation doesn't matter, and thus that it can perform optimizations that a Java compiler can't. And this is why functional programming in Java is dangerous. Because Java isn't a true functional language, the JIT and javac can't optimize functional constructs as aggressively and efficiently as they can in a real functional language. Standard functional operations like returning infinite lists are death for a Java program. That's why functional programming in Java is dangerous.

You may object that I've set up a straw man here. OK, you can't return a list of all the integers (or even all the ints) in Java; but surely no one would really do that. Let's look at a more realistic approach. Here again I use recursion to compute the squares rather than a loop:

public class Squares {
    
    public static void main(String args[]) {
        squareAndPrint(1, Integer.parseInt(args[0]));
    }
    
    public static void squareAndPrint(int n, int max) {
        System.out.println(n * n);
        if (max > n) {
            squareAndPrint(n + 1, max);
        }
    }
    
}

That will run. But now suppose I don't want the first 25 squares but the first 25,000:

Ooops. Stack overflow. This is why in XOM I was very careful to use loops rather than recursion, even in places where recursion was much clearer. Otherwise a carefully configured XML document could have caused a XOM-using program to dump core. Avoiding arbitrarily large recursion in non-functional languages like Java and C isn't just a performance requirement, it's a security requirement too!

Now before the flames begin, let me be clear about what I am not saying. I am not saying that functional programming is a bad idea. I am not saying that functional programming is inefficient. I actually love functional programming. Like my colleagues I find find functional programming to be a natural, intuitive, and beautiful style of programming but only when it's done in a language that was designed for it from the beginning like Haskell. Functional idioms in Java are performance bugs waiting to bite you.

77 Responses to “Why Functional Programming in Java is Dangerous”

  1. Rainer Joswig Says:

    > The answer is that Clojure, like pretty much all true functional languages (and unlike Java) does lazy evaluation.

    Clojure does not do ‘lazy evaluation’. Clojure uses strict evaluation, like Lisp, Scheme, SML, OCAML, Erlang, …

    Clojure supports lazy lists. As a data structure – not in evaluation.

    See also http://en.wikipedia.org/wiki/No_true_Scotsman

  2. jujuju Says:

    Shity article
    do you know Iterator ?

  3. Marcel Valdez Says:

    Functional programming in Java is as dangerous as Parallel and Concurrent programming is in Java.

    Both will tend to be hard to understand and have hidden bugs. But that doesn’t mean that it is *always* a bad idea. And both will require careful implementation from the programmer.

    Just as it is a bad idea to unnecessarily use Parallel & Concurrent programming in Java; it is also a bad idea to unnecessarily use functional programming in Java.

    I think this is the point of this post. But you wrote the title as if it was a single universal truth. This reminds me of the ‘[XXX] considered harmful’ articles.

  4. carlok Says:

    For F’s (Frank Umberto Clark Kennedy’s) sake, those saying that “Java 8 will solve this”: haven’t you noticed the pattern ? Java 6 will fix ___, Java 7 will fix ___, Java 8 will fix ___, Java N will fix ___, Java N+1 will fix ___ …..

    In the meantime, you wasted YEARS programming in (and defending) an UNFIXABLE BRAINDEAD PARODY OF A LANGUAGE/RUNTIME/ECOSYSTEM, whilst you might have saved yourselves the troubles and used a sane one.

    Of what use is that JVM can be fast in microbenchmarks when you kill that advantage with layers upon layers of needles abstractions and wrappers and libraries that allow you to do ___ when this can be done in a few lines in a sane language ?

    In the end a LARGE applications written in Ruby/Python/Tcl/$whatever$ can be faster then Java ones (and more flexible / maintainable) because you don’t need shitloads of useless boilerplate nor tons of XML, there are no pointless hoops to jump through, just plain solution which even slower runtimes can execute faster than JVM will execute your mammoth blobs.

    ALSO:

    All idiots crying “This is not how you would do this in Java, … yada yada”: it seems to me that this was the point of the post, which you somehow missed. But missing the point is perhaps to be expected from average [Java] code monkey.

  5. MeMySelfAndI Says:

    @OP: Please do not use straw-men arguments, those are ONLY reserved for people trying to push (shiny new feature) into Java.
    Otherwise I agree with those writing that if you want functional programming, please go use a functional language and stop trying to screw up our Java. It is not perfect and I bet that other cool language you were using did everything sooo much better. If it was that great, why not stay with it?

  6. Sequências Infinitas em Scala e Java « Mente de Iniciante Says:

    […] Recentemente, o polêmico Uncle Bob tem escrito sobre como utilizar conceito de Programação Funcional em Java e tem recebido diversas críticas. […]

  7. diego Says:

    Venkat Subramaniam solution -> http://blog.agiledeveloper.com/2013/01/functional-programming-in-java-is-quite.html

  8. FFFFFF Says:

    “Functional idioms in Java are performance bugs waiting to bite you. ”

    Nope.

    – You can always compromise and use loops instead of tail recursion, and still have mostly “functional” java code. If you do this there is no “performance bugs”
    – Functional Java (http://functionaljava.org/) has no problem with recursion
    – If you actually need recursion (i.e. the function cannot be written tail-recursively) the most natural way is to use the stack instead of making an explicit stack. Turn up the stack size.

  9. Semiografo Says:

    I wonder why functional programming is considered more elegant than other paradigms. The right question is: more elegant where? For traversing graphs? For japanese people or anyone else who read from right to the left?

    Sometimes I think functional programmers are pleased by the fact that only them can understand their code. Functional code produce much less source code than procedural’s for doing some complex scientific algorithm. In fact, functional code almost mimics mathematical notation. On the other hand, procedural is more algorithmic in the sense that you can organize your solution as an ordered sequence of tasks – and not a reverse order or a fixed-point trick making every stuff anonymous (the fewer the identifiers the better for functionalists).

    That said, I love Javascript, the convergence between the functional and procedural worlds. You can do anonymous recursions with something like a Y combinator or even making something more legible like encapsulating named recursive functions inside another (outer) function which holds static parameters in relation to the inner function.

  10. EricH Says:

    Clojure, Scala, Haskell, et al, would all also crap out if you write code that forces them to store all positive integers in a list. That’s not what they’re doing. The example you cite uses method which creates a list of numbers in a range. Being lazy has nothing to do with it. You could extract the list to a concrete immutable and be fine. You create a list of all integers, and then (would without OOM exception) *use* the first 25 of them. Functional is a style. Java can be used to write purely functional code… it’s just painfully verbose.

    Guava is one such library that adds a number of functional pattern support, including lazy evaluation of transformations of collections. With a bit of finagling, one could implement them from scratch though. (for some reason the forum is eating my indents…)


    public List nameList(List people) {
    return Lists.transform(people, new Function() {
    public String apply(Person person) {
    return person.getName();
    }
    });
    }

    Whereas e.g. scala has a lot of support for functional idioms, largely due to first class functions and convenient shorthand:


    def nameList(people: List[Person]) = people.map(_.name)

    Same method, less headaches. 🙂

    As for why the java std lib doesn’t have a Range class, I have no idea. This is a simple way to create one:

    http://stackoverflow.com/a/6828887/1057157

  11. Bret Says:

    I don’t think most of the commenters can read, or else they are choosing to grind another axe off this article.

    The author writes: “Every few months I find myself debugging a production problem that ultimately traces back to a misuse of functional ideas and algorithms in a language and more importantly a virtual machine that just wasn’t built for this style of programming.”

    That means he DOES see these sort of mistakes being made by professional programmers, and he’s writing about it for that reason. All the comments along the lines of “no one does that” are obviously not correct. The professional coders he works with DO, and that’s what he’s writing about, and why he’s writing about it.

    He does provide a contrived simplifiled example to illustrate what he’s writing about. Probably to make it quite clear. He doesn’t provide actual code written by one of his colleages – probably becuase it’s copyright by the company, and he can’t, and perhaps because he doesn’t want to single out one of his colleagues for the level of derision evident in the responses to this article.

    But as he clearly stated, he does see this sort of thing in professional code, and so it’s quite legitimate for him to piont it out and discuss it.

    In the comments I read quite a lot of “I don’t code like that!”. That’s good, I’m glad you don’t. But as he clearly states, some people do, and the article is written for them. For people who do write their code like he’s illustrating, this is a useful article – this may help show them what they obviously don’t know yet.

    For those of you who know better already, he has clearly not directed his comments at your code, and many of the responses come across to me as either poor reading comprehension, or perhaps evidence of insecurity. He didn’t write that you do these things – he wrote that some of his colleages do.

  12. Ben Gillard Says:

    Basically agree with OP, although he has phrased it too absolutely in places I think. I partly agree with Christian Kohlschütter’s criticism in that respect – a novice programmer or casual reader might get the wrong message if they don’t read the fine print.

    OP says (or implies) that it can be “dangerous” to _naively_ bring functional assumptions, patterns and idioms to Java which is by design, by paradigm, by mindset an imperative environment. (By environment I mean the JVM, the language, most libraries, dev tools, and the intent of its designers) With that, I agree.

    Even Clojure and Scala, which are designed from the beginning to be functional languages, have to make some compromises and incur some awkwardness or verbosity (“recur” for example) because in the end they run on the JVM which is designed for imperative rather than functional programming. (From wikipedia: “Limitations in Java bytecode complicate tail call optimization on the JVM”). Comments from Java fans that “functional programming is coming in Java 8”, or that the JVM might be getting a TCO enhancement, miss the wider point, I think.

    Osvaldo Doederlein made some excellent additional (perhaps stronger and more general) points, imnsho, about performance and optimisation. (Recall OP’s original gripe was poor performance rather than outright stack overflow or heap exhaustion.)

    I’d like to add one other point which is nothing to do with code performance. Each language has a “way”. Rubyists talk of “the Ruby way”, for example. That is, to solve a particular kind of problem or implement a particular abstraction, an idiom has emerged from that language’s community which is The (“Best”) Way of doing it in that language. Furthermore, moderately experienced users of that language will recognise that Way when they see it in someone else’s source, and can quickly understand it at a higher/more abstract level without having to spend too many brain-cycles parsing the basic syntax or following the code path to discover its intent.

    It seems to me, for example that in many functional languages loops are usually coded with either a built-in iterator or recursion, but in imperative languages a for or while loop would usually be used. Someone who’s raised on conventional Java alone is likely to be confused (cache miss? page fault? skip a gear?) by a loop in Java which is coded using recursion. Not saying they won’t work it out eventually, but it takes extra effort. What are the initial and stopping conditions? What is the loop counter/variable? And of course it goes the other way too: You can usually do imperative things in functional languages but they often won’t be as elegant or efficient as The (functional) Way, and they may also confuse other coders who are reading the source in a functional frame of mind.

    A meat-space parallel might be the different human cultures. In some countries, business discussions are usually quick and aggressive, in others you chat over coffee for a few hours first. If you try to do it the ‘wrong’ way in either country, you may still get your business done, but things just won’t be as smooth and the result might not be as favourable.

    Interesting discussion. Thanks to all posters.

  13. David Marsh Says:

    @Bret, then point his colleagues at Guava or FunctionalJava libraries and tell them not to roll their own code with bugs.
    Tell them to write decent JUnit tests that will show up stack overflow and possibly highlight poor performance before production.
    Tell them to download the latest Java 8 SDK and learn the new features.
    Tell them to order this book and read it:- http://pragprog.com/book/vsjava8/functional-programming-in-java

    Lastly tell your boss you aren’t doing production support and bug fixing for Ivy league muppets unless they listen to the above advice.

    Then go enjoy your less stressful life…

  14. explosiveamber Says:

    Excellent article. I appreciate this piece of information as I currently program in a lot of functional languages, but I need to resort to Java to build an experimental application for scientific computing. The tipping point, past which of it is not safe to use functional paradigm to solve a problem is already found in small-size problems rather than in medium. Analysis will let me predict the cost of each operation, but not the amount of input data that others will use. That’s why they – and I – will hit the wall.

  15. rhg135 Says:

    I do agree with the OP in that it’s dangerous in java. The language can let you write atrocious code (as you showed) that’s neither functional or procedural. @carlok, for “F”‘s sake get your facts straight, the JVM is no parody, at the time there was nothing like it.

  16. mschaef Says:

    I recently spent a few weeks converting a tree-query language written in Java from an imperative implementation to a functional, streaming implementation. The technique I used is similar to what many of your comments have suggested: my implementation used Iterables and Iterators to provide a lazy stream abstraction. My first comment based on this experience is that it’s quite possible to safely write code in a functional style in Java (I was using JDK6 ported to JDK7, so none of the fancy JDK8 stuff). My second comment is that the experience felt a lot like writing procedural code in assembler or object-oriented code in C. The language does have the ability to express functional designs, but with a heavy syntactic overhead. For me, and the system I was developing, it only made sense because the total amount of code was relatively low, and the benefits were very high. (It was an interactive query language, so it was very important that the query language produce its first result quickly. Switching to stream based processing both achieved this goal and kept memory usage WAY down in many of our more important scenarios.)

  17. A Java Dev Says:

    I think you are wrong on many instances.

    This is official Java 8 , look how it is short and beautiful, does exactly what you want.

    import java.util.*;
    public class Main
    {
    public static void main (String[] args)
    {
    List numbers = new ArrayList();
    for(int i = 0; i System.out.println(e*e)); // functional
    }
    }

  18. A Java Dev Says:

    import java.util.*;

    public class Main
    {
    public static void main (String[] args)
    {
    List numbers = new ArrayList();

    // populate list

    numbers.forEach((Integer e) -> System.out.println(e*e));
    }
    }

  19. A Java Dev 2 Says:

    Even more easy in Java 8.

    public static void main (String[] args)
    {
    List integers =IntStream.range(0, 25).boxed().collect(Collectors.toList());
    integers.forEach( e -> System.out.println(e*e));
    }

  20. Java? ??? ?????? ???? ???? ?? ?? | Just hack'em Says:

    […] ??? ????? ? Why Functional Programming in Java is Dangerous? ??? ?? ?????. ??? ??? ?? Clojure ??? Java? […]

  21. Henk van Voorthuijsen Says:

    Or even more compact:

    public static void main(String[] args) {
    IntStream.range(1, 25).map(n -> n * n).forEach(System.out::println);
    }

  22. orbfish Says:

    Java 8 Streams.

  23. Bad Programming in Java Is Dangerous | Dinesh Ram Kali. Says:

    […] a blog post this morning titled Why Functional Programming in Java is Dangerous. Author Elliotte Rusty Harold sets up the world’s worst straw man I’ve seen. He talks about […]

  24. MWF@Nexient Says:

    I came across this and read through the comments and articles. I came to one conclusion: This article is a counter to “Trust me, I know what I’m doing.” Specifically, it’s this line in Robert C. Martin’s article:

    “The function call (integers) returns (1 2 3 4 5 6 …). OK, I know you’ve got questions about this, right? I mean, how big is this list? The real answer is that the list is only as big as I need it to be; but let’s not think about that just now. For the moment just accept that (integers) returns (1 2 3 4 5 6 …); because it does!”

    The implication is the collection of integers returned is from 1 to the largest possible. THEN a new list is made that is the square of all those integers. THEN we take the first 25 of those. In Java 8, that would be:

    IntStream.rangeClosed(1,Integer.MAX_VALUE).map(n->n*n).limit(25).forEach(System.out::println);

    Now, when people say “no reasonable Java programmer…”, they miss the point. They suggest things that do not do what is implied. They create iterators, or streams that that are limited from the beginning. They use boolean values in calculating Integers. In other words, they are breaking the requirements. Each example fails to counter simply because they deviate from the original design. Are they improving upon it? Perhaps, but the goal was not to improve, which the very first java snippet did, but to replicate as Uncle Bob described.

    This article should be read as “You should not use functional programming designs in Java, so use OOP programming designs,” which is what he wrote.

    There is one major problem in his example and that’s his integers() method. The loop overflows so it’s always less than or equal to Integer.MAX_VALUE. Still causes a heap space error when corrected to i<Integer.MAX_VALUE and then add MAX_VALUE after the loop.

  25. Meh Says:

    The proper route is: Java 7 + Scala not Java 8 as giant sandwich. OOP and Functions don’t belong together in Java!

  26. Why Functional Programming in Java is Dangerous – JUGLviv Says:

    […] Why Functional Programming in Java is Dangerous […]

  27. haiyang li Says:

    I think the correct way to do it is: IntStream.range(0, Integer.MAX_VALUE).map(i -> i * i).forEach(System.out::println);

Leave a Reply