Wednesday, January 23, 2013

Functional Programming in Java is quite Approachable

Let's avoid Fear and Confusion

In the past few days number of tweets pointed to this blog: "Why Functional Programming in Java is Dangerous."  Several people have commented on it already, I'd like to present here a pure Java example that solves the problem discussed in that blog.

I would totally agree with the title of that blog if three words are removed from it: "Functional", "in", and "Java". The blog simply showed that programming can be dangerous, especially if we use the tools provided by a language for what it's not intended.

Disclaimer: I will admit, I am often critical of languages and have called out on things I find deficient. Java has been on the top of that list many times. At the same time, when I see a feature well done, I don't hesitate to speak in favor of it as well. I sincerely try not to carry any biases against languages, but try to be as objective as possible.

I have been playing with the lambda expressions facility in Java and have only nice things to say so far.

The blog mentioned above talks about functional programming, but the examples are really dealing with lazy collections and tail call optimization (which benefit from the functional paradigm), both of which are quite possible and elegant in Java 8. Let's get to the examples.

A Word of Caution

This blog is quite terse, I am not explaining much here, the intention is simply to show what's feasible.

How to run the code examples below

You'd need Java 8 with support for lambda expressions to compile and run the code below. Please download from http://jdk8.java.net/lambda/. The code below was compiled using build b-74.

Square of integers

Java 8 has an interesting interface called Stream which provides a lazy collection. Let's use that to build square of integers mentioned in that blog.

import java.util.stream.Streams;

public class Sample {
  
  public static void main(String[] args) throws Exception {
    Streams.iterate(1, number -> number + 1)
    .map(number -> number * number)
    .limit(25)
    .forEach(number -> System.out.print(number + " "));
  }
}
//output:
//1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625  

That's the complete code.
In the above code the iterate method of the Streams class is a higher order function that takes in a seed value (1) and a generator. The map method is lazily evaluated and is delayed until the call to limit method arrives. Each time the generator is called (and it's called 24 times in this example), it returns the next number in the series. Using this we created a series of numbers 1, 2, 3, 4, ... and then the map method simply creates a square of those numbers.

Tail call optimization

Tail call optimization is trivial once we get a hang of the lazy infinite collection. Rather than thinking about it as calling the function repeatedly while holding on to the stack, let's think of it as a collection of unknown number of function calls that we can lazily evaluate as many times as we like. Once we wrap our head around this problem as a lazy evaluation of an infinite collection, it simply fizzles down to minimum code.

Let's use the same example as in the other blog, tail recursion of squareAndPrint method. You can run the code below with as large an input as you like, 25000 for example, and will not get any StackOverflowError.

import java.util.stream.Streams;

@FunctionalInterface
interface TailCall {
  TailCall get();
  default boolean terminated() { return false; }
}

class TailCallTerminate implements TailCall {
  public TailCall get() { throw new Error("Don't call"); }
  public boolean terminated() { return true; }
}

public class Sample {  
  public static TailCall squareAndPrint(int number, int max) {
    System.out.println(number * number);
    if(max > number) {
      return () -> squareAndPrint(number + 1, max);
    } else {
      return new TailCallTerminate();
    }
  }
  
  public static void main(String[] args) throws Exception {
    int max = Integer.parseInt(args[0]);
    
    Streams.iterate(squareAndPrint(1, max), TailCall::get)
    .filter(TailCall::terminated)
    .findFirst();
  }
}

We're creating a functional interface called TailCall here. The squareAndPrint method returns either a next call (packaged into a TailCall implementer) or indicates the recursion is over by returning a TailCallTerminate instance. We can query the instance returned to know if we should continue or stop.

To begin with we have no clue how many tail calls exist. We turn this into a lazy infinite collection using the Streams' iterate method. We continue with the recursion, iteratively that is, until we reach the terminating call.

The above two examples shows what's feasible in Java. We could do this today with anonymous inner classes, but that would drive us insane. With lambda expressions just around the corner, we can write quite a bit of elegant code in Java.

Java is not a perfect language, it has quite a bit of ceremony, and we have heard several complains (including some from yours truly in the past). However, Java 8's functional style of programming is giving new hopes to millions of Java programmers who're putting it to good use each day. I see this to be a step in the right direction for Java, I am quite optimistic and will not hesitate to program with it, in functional style of course.

14 comments:

Ken Sipe said...

bravo... nice post!

Stephen Souness said...

Given that Java 8 with the new functionality that is used extensively in your examples has not yet been released, would it be fairer to state "Functional programming in Java _will be_ quite approachable"?

It's not as catchy, but let's not forget a lot of Java developers out there haven't made it to Java 7, let alone Java 8.

Venkat Subramaniam said...

@Stephen I agree that whether Java 8 itself is approachable or not depends on various other factors. The adoption of Java 5 took shockingly long time for a large number of companies. Java 5 to Java 6 and 7 seems relatively better. With Java planning release every two years starting with 8, let's hope that trend changes for the better.

Mohammad Shamsi said...

Hi,

I'm having a compile issue with the first example on my PC.

It's complaining about ambiguous call to map.

Any idea?

Jdk1.8-74b-64bit-Linux

Venkat Subramaniam said...

@Mohammad,

Not sure why you're getting the error on call to map. You may specify the type explicitly for the lambda expression that is passed to the map method. This may help resolve the ambiguity. I am using the mac os x version of build 74 and not seeing the error you're experiencing.

Mohammad Shamsi said...

@Venkat,

I already tried specifying the parameters type but did't help.

btw, If I use a list of String, instead of integers, it is successfully compiled and the result is as expected.

I guess, only numeric list having this issue.

Thanks btw.

Venkat Subramaniam said...

@Mohammad

Hum, not sure why this is different between the versions on Mac vs. linux. I'm sure all this would get resolved by the final release. Thanks for suggesting the change that worked on your side, I'm sure it will help others who may be trying it.

Unknown said...

Really interesting post!

Seth with Firebox said...

Truly a remarkable post. You are right when you say "programming can be dangerous, especially if we use the tools provided by a language for what it's not intended" It is up to the developer to use the tools judiciously and for the right purposes.

Lisa Edward said...
This comment has been removed by a blog administrator.
Bala Murugan said...
This comment has been removed by a blog administrator.
rebeka christy said...

Thanks for sharing your innovative ideas..Its really useful and interesting...


Oracle Training in Chennai

Tip Seo said...

GREAT POST
THANKS FOR IT
Java Training In Chandigarh

Anbukarasi J said...

very useful... beginners see this http://www.javatrainingchennai.in/ as well to get some basics