Monday, July 5, 2010

Lambdas in Java Preview - Part 2: Functional Java

This is the second part in a series of blog posts (read part I) giving some practical examples of lambdas, how functional programming in Java could look like and how lambdas could affect some of the well known libraries in Java land. This part focusses on general functional programming techniques, which will be available through the addition of lambdas. Functional programming (although I still wouldn't consider Java a functional programming language) will make Java code more concise, more expressive and more readable in certain kinds of problem situations.

Note: All the following examples work with the current prototype implementation of lambdas.

Simplifying code
Let's start with a simple example that shows how higher-order functions in general can lead to simplified code. Suppose we want to search for files of a certain type. Up to now, we would probably use a FileFilter:
File dir = new File(".");

File[] files = new File(".").listFiles(new FileFilter() {
    public boolean accept(File file) {
        return file.getName().endsWith(".java");
    }
});
But with higher-order functions we could write a method that takes the filtering logic as a function argument. Think of this code as being part of an API.
public static File[] fileFilter(File dir, #boolean(File) matcher) {
    List<File> files = new ArrayList<File>();
    for (File f : dir.listFiles()) {
        if (matcher.(f)) files.add(f);
    }
    return files.toArray(new File[files.size()]);
}
The client code of this API would be a one-liner then:
File[] files = fileFilter(dir, #(File f)(f.getName().endsWith(".java")));
Callback mechanisms like filtering files with a FileFilter like above can generally be greatly improved by replacing the callback class with a function. And in fact, callbacks with only one method are so common in Java, that there will be special support for so called SAM types (single abstract method, see also first part of this series) from project lambda. This allows us to call the existing File.listFiles(FileFilter) method with a function instead of a FileFilter. Because FileFilter is a SAM type, the function will automatically converted into a FileFilter then:
File[] files = dir.listFiles(#(File f)(f.getName().endsWith(".java")));

Currying
One of the features most functional languages support (some even make very excessive use of it) is currying. Currying allows for defining a function with multiple parameters and allow the client to call it with fewer parameters, which will return a function in which the given parameters are fixed. A simple example would be a function that takes two parameters x and y and returns the sum. The client could the call this function with a fixed value for x, say 5, but without a value for y. This would result in a function that takes one parameter y and returns 5+y. Let's try to implement this curried function in Java:
##int(int)(int) sum = #(final int x)(#(int y)(x+y));
assert sum.(3).(5) == 8;

#int(int) add5 = sum.(5);
assert add5.(4) == 9;
Here we first define a function sum of type ##int(int)(int), i.e. of type function that takes an integer and returns a function of type #int(int). We can evaluate this in a single statement sum.(3).(5). The first call sum.(3) will basically result in a lambda expression #int(int y)(3+y). The second call will evaluate this expression passing 5 as the value for y. But instead of evaluating the whole expression, we can also evaluate it partially, as in line 4. This will result in a function add5, that could be called with different values of y and would return 5+y.

That's what currying in Java could look like. Agreed, it looks nicer in other languages, but it generally works. Maybe some syntactical sugar could still be added.

Control Abstractions
Another feature of many functional languages is the possibility to build control abstractions that look like natural syntax. This is made possible through currying and higher-order functions in general. Let's have a look at a simple example.
##void(#void())(int) afterDelay = #(final int n) {
  return #(#void() f) {
    try {
      Thread.sleep(n*1000);
      f.();
    } catch (Exception e) {}
  };
};
afterDelay is a higher-order function, it takes an integer n as argument and returns a function that takes a function f. The returned function will execute f after n seconds. We can invoke it directly like
afterDelay.(2).(#()(System.out.println("foo")));
which will print "foo" to the console after waiting for two seconds. Or we can make use of currying to create another function after5Seconds, which is a function that takes another function and evaluates it after waiting for 5 seconds.
#void(#void()) after5Seconds = afterDelay.(5);

after5Seconds.(#()(System.out.println("foo")));
after5Seconds.(#()(System.out.println("bar")));
Now this still looks like a normal function call and not like a control abstraction, yet. But it is simple to make it look like one. Just replace the parentheses with curly braces
after5Seconds.(#() {
    System.out.println("foo");
    System.out.println("bar");
});
and it almost looks like other control structures like for or if.

Now, let's have a look at another example that demonstrates the loan pattern:
##void(#void(PrintWriter))(File) withWriter = 
  #(final File file) {
    return #(#void(PrintWriter) doWithWriter) {
      PrintWriter writer = null;
        try {
          writer = new PrintWriter(file);
          doWithWriter.(writer);
        } catch (Exception e) {
          // Just ignore exceptions, see straw-man
          // proposal for exception handling with 
          // lambdas
        } finally {
          if (writer != null) { 
            writer.close();
          }      
        }
    };
};
Simply said, withPrintWriter is a function that takes a file as its argument and opens a PrintWriter on that file. Then it calls a function given by the caller with this PrintWriter (or loans it to the function). Things will probably get a lot clearer having a look at how to call withPrintWriter.
File file = new File("log.txt");

withWriter.(file).(#(PrintWriter writer) {
    // Printing to the file
    writer.println("foo");
    for (int i=0;i<10;i++) writer.println(i);
});         
As withWriter is a curried function, we can evaluate it partially (just with a file) and assign the result to a variable of function type, which can be called multiple times.
#void(#void(PrintWriter)) logger = withWriter.(file);

logger.(#(PrintWriter writer) {
    writer.println("foo");
});         
logger.(#(PrintWriter writer) {
    for (int i=0;i<10;i++) writer.println(i);
});
Note, withWriter can also be implemented as a non-curried function. We could pass the function directly as a second argument to it.
#void(File, #void(PrintWriter)) withWriter = 
  #(final File file, 
      final #void(PrintWriter) doWithWriter) {
    PrintWriter writer = null;
    try {
      writer = new PrintWriter(file);
      doWithWriter.(writer);
    } catch (Exception e) {
    } finally {...}  
  }
};

withWriter.(new File("log.txt"), #(PrintWriter writer) {
  writer.println("Hello ");
  writer.println("World.");
});         
This kind of control abstractions can be very useful for code reuse, e.g. to remove boilerplate code in resource handling (withWriter, withReader, ...) or transaction handling (inTransaction).

For the fun of it let's do another example, which simplifies reading the lines of a file one by one:

#void(File, #void(String)) eachLine = 
  #(final File file, final #void(String) fun) {
    FileInputStream in = null;
    try {
      in = new FileInputStream(file);
      BufferedReader reader = 
        new BufferedReader(new InputStreamReader(in));
      String line = null;
      while((line = reader.readLine()) != null) {
        fun.(line);
      }
    } catch (Exception e) {
    } finally { /* close in */ }      
  }

};
Again, think of eachLine as being part of an API, hidden from the client. On the client side things get really easy.
File file = new File("log.txt");

eachLine.(file, #(String line) {
  System.out.println(line);
});
In fact eachLine should probably be part of the class File itself. And maybe it will get in there in JDK7 via an extension method (which is a topic of a following post).

Originally, I didn't plan to write that much about control abstractions (it's just too much fun, though), but instead cover recursion as a functional technique, too.

Recursion
Unfortunately, until now it's not quite clear how recursive calls in lambda expressions would look like or if it will even be possible to write recursive lambdas (if it wasn't possible we could still call recursive methods from a lambda). There are currently some discussions going on the project lambda mailing list, so I'll skip this topic for now.


To summarize, functional techniques allow for some code simplifications in certain scenarios as described above. Actually, this was also the primary reason to introduce lambda expresssions, namely for simplifying code for the new fork/join framework and parallel arrays. But lambdas make it possible to simplify code in a lot of other use cases as well.

8 comments:

Chris Brind said...

What crazy fools really want this in Java? It's fugly, unreadable and it's going to be a nightmare to maintain.

paul youngh said...

Cool! This really has the power to remove lots of boilerplate code. Writing it may look a little complicated at first, but using the result by the client looks great!

steve said...

It really looks like that Oracle wants to break the Java community apart.

Not only in two halves, but quite likely three:

- Those who will never use closures and will choose their libraries in such a way so that they don't have to bother with them.

- Those who think they have have to show off how intelligent they are, trying to use it on every possible and impossible occassion.

- And finally those, who just use Scala, which had:
a) nice looking and working closures from the beginning
b) an API which actually uses it

If Oracle really decides they want to include closures in Java 7, at least implement them in a Scala-compatible way.

(But I guess it's useless to hope they will. Just like Sun, they will choose the implementation, which causes as much trouble with other languages as possible. Not that another language could compete on the JVM with Java, god forbid!)

Anonymous said...

Comparing this to C# the syntax is ugly and unreadable. E.g. in C# you write col.Find( x => x.Name == "John"); or perhaps execute foo for all items: col.All(x => foo(x));

Anonymous said...

seriously, this looks bad. C# has done a good job of this already.

I'd say lets just agree Java as a language has reached its point and cannot be stretched further. C# is a fine legacy it has left.

If you want to create Java++ then do that - but start afresh instead of all this crazy compromise.

Nick Wiedenbrück said...

There has been published a new proposal with a different syntax by now, an it uses a lot more type inference. Then it looks almost as the C# lambdas. Stay tuned.

Anonymous said...

Is it just me or is this plain nasty? Who came up with this syntax? I hope that Oracle takes a look at the Groovy syntax before they decide to send Java programmers into years of depression.

Anonymous said...

@Anonymous (July 19)
It's not just you -- this syntax is hideous. Not only are there reasonable alternatives in languages like Scala, Groovy, and C#, but there is even a better option that has already been explored for Java:

http://www.javac.info/

http://gafter.blogspot.com/2010/02/syntax-option-for-project-lambda.html