Nick has been a passionate Java developer for 10 years now. In his day job he is working as an enterprise Java web developer. In his spare time he likes to learn new programming languages and new Java related technologies, and he regularly posts articles on his blog. Nick is a DZone MVB and is not an employee of DZone and has posted 7 posts at DZone. View Full User Profile

Lambdas in Java Preview - Part 2: Functional Java

07.19.2010
| 5409 views |
  • submit to reddit

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.

 

From http://stronglytypedblog.blogspot.com/2010/07/lambdas-in-java-preview-part-2.html

Published at DZone with permission of Nick Wiedenbrueck, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Marc Stock replied on Mon, 2010/07/19 - 11:43am

The syntax of lambdas in Java is so horrid I'd rather use the SAM technique. At least SAM is readable (albeit more wordy).

Thomas Eichberger replied on Tue, 2010/07/20 - 1:10am

I wished they would forget the #() syntax and define it all as in Groovy...

Honey Monster replied on Tue, 2010/07/20 - 4:25am

Control abstractions are nice. Unfortunately, the current proposal makes them a lot less nice as the "lambdas" (not closures) must live under the same restrictions as methods in anonymous inner classes.

Which means that the "body" of your client side code cannot reference mutable variables, fields or parameters from the enclosing block. Like with anonymous inner classes, fields must be declared final for the lambda to use them as "free" variables.

The current proposal will not really allow functional programming. It has too many restrictions and shortcomings. This is because the primary goal of lambdas right now is to enable a single use-case: More granular parallel programming. After dissing closures for so many years, Sun(Oracle) has finally woken up and realized that to get beyond the notoriously difficult thread-based parallelism, you need something like closures.

Working under time constraints (remember this was promised for JDK7 - and they are still not clear on what exactly we will get), they are throwing away features because too many nasty issues tend to crop up.

One such issue is - tada - checked exceptions. How do you deal with a lambda which calls methods which declare that they'll throw checked exceptions? Again we are reminded that a checked exception is an implementation detail which has become part of a contract (the method signature).

This is probably the reason why function/method references (delegates in C#) has been dropped from the latest proposal: Checked exceptions are part of the method signature and thus would have to be part of the function type. By dropping function types altogether and reverting to SAMs the language designers avoid the issue.

Because delegates/closures have been ignored until now in favor of anonymous inner classes, the JDK already has been designed with a number of different "filters" specifically for each use case, even though they are exactly like each other. A "predicate" may be implemented using a SAM - but each part of the API have had to define its own set of SAMs. Methods which takes a "filter" as a parameter expects its own specific SAM type.

Thus it becomes unwieldy or impossible to design something like the generic or for filters. If you want to compose predicates you will have to implement the 'or' for each and every SAM predicate type.

As Jesse Kuhnert said on the lambda-dev list:

Indeed, I have a feeling that anyone with a keyboard and search engine is going to have a hard time matching up the current proposal with the promise of closures - if and when it does make it out into the wild.

Java developers get ragged on enough as it is, no one can say for ~sure~ what the reaction will be except maybe time and loss of market share/respect.

Honey Monster replied on Tue, 2010/07/20 - 4:32am in response to: Thomas Eichberger

I wished they would forget the #() syntax and define it all as in Groovy...

They did. The syntax has already changed to { ... -> ... }. Still with the caveat that it may not be the final syntax, though.

And to be honest, although syntax is interesting and important it seems to be the least of the problems right now. The current proposal is nothing like closures as in Groovy, Ruby or C#.

Howard Lovatt replied on Tue, 2010/07/20 - 5:52am

The lambda spec. is still very much evolving, the latest draft is: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-2.html This specification is very different than the older spec. you are writing to and the one in the current build of JDK 7. Unfortunately none of your examples will work with the latest draft. The latest draft drops the #(...){ ... } syntax and instead uses { ... -> ... } and it also drops the lambda types and instead you use a SAM.

Thomas Kern replied on Thu, 2012/09/06 - 10:56am

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

http://www.java-tips.org 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.