Concepts of programming languages / Java 8

Is Java 8 a Functional Programming Language?

Last update on

A surprising observation

I'm pretty excited about Java 8 most important feature: lambda expressions. I can think of so many ways to utilize them I can hardly wait until I can use Java 8 in vocational life. Lambdas stem from the world of functional programming languages, so obviously Java 8 is a functional programming language. It's in the same league as languages like LISP, Clojure and Scala.

But wait - is this true?

Some ten months ago I used Java 8 to write a Mandelbrot set plotter. No matter how hard I tried, the program wouldn't become functional. It was just a good old procedural program using a couple of lambda expressions. It can't be the algorithms fault: Mandelbrot sets are all about functions. So what is it that stops Java 8 from being a functional language?

Effectively final: closing over values instead of variables

Project Lambda's decision to dismiss full-blown closures has been criticized a lot. To avoid confusion and disappointment the team members call their closures lambda expressions. Most people expect closures to be able to modify variables defined outside the closure. A closures variable scope "closes over"1. the scope the closure is defined in.

Java 8 lambda expressions can access variables of the outer scope if they are final. Lambdas don't close over variables, they close over values. This definition allowed Project Lambda to slightly loosen the requirements: lambdas are allowed to use outer scope variables if they are not formally declared final. It suffices to ensure they are not changed (they are "effectively final").

Is Java 8's failing to be a functional language something to do with the lack of full-blown closures?

No. Quite the contrary. No doubt about it, full-blown closures add a lot of value to a language (plus a bunch of problems, by the way), but they don't make a language functional. Full-blown closures can even be considered an obstacle on the way to a functional language. Functional programming isn't about variables. It's about functions. Most functional programming languages don't encourage the use of variables. For instance, many Scala programs do almost without variables. Scala programmers typically prefer "val" over "var". It may sound weird to seasoned programmers of Java, Visual Basic, C/C++ or any other of the tradtional languages, but it is perfectly possible to write programs lacking variables. In fact, there are even languages like LISP that don't know the concept of a variable.

Truth to tell there are languages that do need variables. It's utterly impossible to write an assembler program without the ability to change memory cells (which can be considered a predecessor of variables). The same applies to pre-object-oriented BASIC and to non-OO Turbo Pascal. However, it should be possible to abstain from using variables with most object-oriented languages including Java.

Range types are missing in Java 8

The one feature preventing my Mandelbrot set program to be functional was Java 8's lack of range types.

Drawing a Mandelbrot set amount to repeating the same algorithm for every pixel of the window. Basically the algorithm looks like so:


for (int x = 0; x < window.width; x++) {
    for (int y = 0; y < window.height; y++) {
        if (mandelbrotFormulaConvergesAt(x, y)) {
            drawPixel(window, x, y);
        }
    }
}

Groovy programmers can convert this algorithm to a more functional style by using range types:


[0..window.width-1] { x ->
    [0..window.height-1] { y ->
        if (mandelbrotFormulaConvergesAt(x, y)) {
            drawPixel(window, x, y);
        }
    }
}

Unfortunately, there is no nice equivalent to 2 or the even shorter version 1024.times{....} in Java 8. At first glance this doesn't seem to be important. But the principal motivation to introduce lambda expressions to Java was to support massively parallel computing. The nice thing about 1024.times{...} is you don't specify the order of the closure invocations. All you know is the closure is executed 1024 times. It can be invoked in any order - including invoking it simultaneously, 1024 calls in parallel.

Java 8 simply doesn't allow for a really concise equivalent of 1024.times{...}. Sooner or later a library will fill the gap. Maybe it'll look like this:


class RangeUtils {
  static void times(int repetitions, Closure t)
  {
    for (int i = 0; i < repetitions; i++)
       invoke(t(i));
    }
  }
}

Even so, Java 8 is going to remain a more clumsy than its Groovy counterpart. The Mandelbrot example looked like that:


RangeUtils.times(window.width, {x ->
    RangeUtils.times(window.height, {y ->
        if (mandelbrotFormulaConvergesAt(x, y)) {
            drawPixel(window, x, y);
        }
    }
}

But it's getting worse.

Lambda expressions are not first class citizens

I've been complaining about the verbosity of the times function I depicted above, but truth to tell it doesn't look that bad. The real problem is you simply can't define it in Java 8.

There is no generic closure type. Closures aren't first class citizens in Java 8.

Java 8 lambda expressions are always mapped to interfaces. Lambda expressions are implemented as SAMs: Classes implementing a single abstract method. So you have to define the times method many times. You need an implementation for each combination of parameters and return types.

Java 8 tries to address the problem by introducing an impressive variety of generic predicate and consumer methods. Chances are you'll never need a predicate method other than the predefined Java 8 methods. However, you need an infinite number of methods to cover every possible case - an impossible task.

Making lambda expressions first class citizens would solve the problem. However, this introduces a lot of new problems, so maybe it was a wise decision to leave the advanced features to the new kids in town. Scala, Closure, Groovy and many other JVM languages already cover this feature, so why should Java give up the advantage its remaining feature: being a blazingly fast language?

Currying

Granted, I doubt currying is used a lot in real-world business applications. Currying is important because it demonstrates an important concept of functional programming, and it shows whether a language can be called functional or not. Languages without currying aren't functional.

As it turns out, Java 8 is functional by that measure. You can do currying. It doesn't even look bad, as the example from StackOverflow shows:


public class CurryingAndPartialFunctionApplication
{
   public static void main(String[] args)
   {
      IntBinaryOperator simpleAdd = (a, b) -> a + b;
      IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;

      // Demonstrating simple add:
      out.println(simpleAdd.applyAsInt(4, 5));

      // Demonstrating curried add:
      out.println(curriedAdd.apply(4).applyAsInt(5));

      // Curried version lets you perform partial application:
      IntUnaryOperator adder5 = curriedAdd.apply(5);
      out.println(adder5.applyAsInt(4));
      out.println(adder5.applyAsInt(6));
   }
}

Update Jan, 29 2014: Currying in Java 7

A reader pointed out that my original example on currying does not show Java 8, but Java 7. Little wonder I found it ugly! In hindsight, I have to correct myself: functional programming in Java 7 deserves my respect. The language wasn't made for this, so it's a lot of boiler-plate code. For the sake of completeness, I add the Java 7 code example I found at Stackoverflow.com:


interface Function1<A, B> {
  public B apply(final A a);
}

interface Function2<A, B, C> {
  public C apply(final A a, final B b);
}

class Main {
  public static Function2<Integer, Integer, Integer> simpleAdd = 
    new Function2<Integer, Integer, Integer>() {
      public Integer apply(final Integer a, final Integer b) {
        return a + b;
      }
    };  

  public static Function1<Integer, Function1<Integer, Integer>> curriedAdd = 
    new Function1<Integer, Function1<Integer, Integer>>() {
      public Function1<Integer, Integer> apply(final Integer a) {
        return new Function1<Integer, Integer>() {
          public Integer apply(final Integer b) {
            return a + b;
          }
        };
      }
    };

  public static void main(String[] args) {
    // Demonstrating simple `add`
    System.out.println(simpleAdd.apply(4, 5));

    // Demonstrating curried `add`
    System.out.println(curriedAdd.apply(4).apply(5));

    // Curried version lets you perform partial application 
    // as demonstrated below.
    Function1<Integer, Integer> adder5 = curriedAdd.apply(5);
    System.out.println(adder5.apply(4));
    System.out.println(adder5.apply(6));
  }
}

As you can see, functional programming has always been possible in Java, but Java 8 is clearly an improvement. But still, it's a far cry from functional programming in Groovy.

Lack of syntactical sugar

While not strictly necessary adding some syntactical sugar might make it a lot more attractive to use Java 8 in a functional way. Apart from the range types I think of short notations of arrays and hash tables:


List<String> trafficLight = ["green", "yellow", "red"];
HashMap<int, String> votes = ["tories": 55, "labour party": 45];

This would enable Java programmers to write lines like


["green", "yellow", "red"].println("Traffic light is %it.");

Doing the same in Java 8 requires quite a few lines, and there's no advantage of functional programming style over procedural programming style. Both require roughly the same number of lines because of the boiler plate code. So many programmers will likely stick to what the procedural style they're used to.


List<String> lights = new ArrayList<>();
lights.add("green");
lights.add("yellow");
light.ad("red");
for (String it in lights) {
    System.out.println("Traffic light is " + it + ".");
}
lights.stream().each( String it ->
    System.out.println("Traffic light is " + it + ".");
}

The stream function

A minor annoyance is the decision to introduce the stream and parallelStream functions instead of adding the functional API to every collection. Granted, introducing these two functions makes interface design a lot simpler because you only have to add just two methods to every collection (instead of several dozen methods otherwise). But having to write ".stream()" adds to the verbosity of the Java language, thereby undermining the idea of using lambda expressions to make the language more concise.

Conclusion

Functional programming has been possible in Java since the introduction of anonymous inner classes. However, anonymous inner classes require so much boiler plate code that the only example of functional programming in Java 7 is the Arrays.sort function. Maybe you can also include the event listeners of Swing and SWT. In most other areas anonymous inner functions are used very reluctantly by most programmers I know. I'm afraid the story is going to repeat again.

The introduction of lambdas will make the Java language a lot more expressive. I'm pretty sure lambda are going to be used a lot more than anonymous inner classes (albeit they are basically the same). But still, the Java language falls short of becoming a functional language. It's almost possible to program Java 8 in a functional way. But there are enough rough edges making it difficult to do so. It bet Java will largely remain a procedural language.


  1. "Close over something" is scientific math jargon and means to "include", "extends" or "contain something" ↩
  2. .1023 ↩

Comments

Comments are temporarily deactivated.