Thursday, May 1, 2014

Lambda Expression (λ-expression) in Java (Part I)

λ expressions are one of the new features introduced in Java 8. This is one of the core features epitomizing Java's realization of providing functional programming features. Java has managed to surprise everyone by withholding this feature for a long time. Almost every other language –  Lisp(s), C++, Python, C#, F#, PHP have supported λ expression in one form or another for at least half a dozen years now.
So, what is a λ? How come such a weird name? Though the roots of the name can be traced back to λ calculus propounded by Church in the 30s, this post is not going to cover the math. Let's save λ calculus for some other day.
A λ expression for all practical purposes is an anonymous function. At first glance it may appear to be quite difficult to digest the notion of defining a function without a name. There can be a multitude of reasons for having this construct, but the primary reason for having a λ is to have code behave as data.

Code as data

If you are a Lisper, you know code is data. For imperative programmers, we will motivate with an example. Callback is age old concept in program design. It's latest popular avtar was AJAX
Let’s say subsystem A calls subsystem B over the network and needs to process the results. But there are no performance guarantees meaning there is no way of telling how long B might take to return the answer. One simple solution in this kind of scenario is to let A continue its work and refactor the code to be executed when B returns in a separate method/function. This code is likely to be unique because no one else needs to see this callback code. Thus, this callback method/function is a prime candidate for being anonymous. Before Java 8, we had to use anonymous classes to hold this method, with Java 8 you can declare and use an anonymous function. So a λ does not really add a new tool in your toolbox, it sharpens the ones you had  since Java 1.1.

Syntax

A run-of-the-mill function/method has these sub-constructs –
  1.   Name
  2.  Return type
  3. Argument list
  4.  Body

A λ does not have name so we are down to representing three constructs – return type, arguments and the body. Before we look at the syntax in Java, let’s look at the Python syntax. Let’s say we want to define a function that returns twice of the argument passed to it. This is how you would do it Python
>>> f = lambda num: num*2
>>> f(100)

Let’s look at the expression lambda num:num*2 . The keyword lambda indicates that this is a λ, stuff before the : is the argument list and the stuff after : is the body of the function. There is no way of squeezing multiple statements in the body of the function. This of course may be a severe handicap. Another noteworthy thing is that the body of the λ has a single expression which evaluates to the return value. There is no explicit return statement but whatever the expression evaluates to is the return value of the lambda.
While we are at it, f = lambda … an anti-pattern, something you should never do unless your purposes are purely instructive. The reason becomes obvious once you find the type of f
>>> type(f)
So, after all we ended up with a function name and this the reason we were able to call f(100)
Java on the other hand has a much better syntax
(comma separated parameters) ->  {
statements constituting the body
}
 OR
(comma separated parameters) ->  expression that would be the return value
There are some bitter rules that one needs to commit to his memory –
  1. Curly braces are mandatory only when you have statements in the function body.  You can get rid of them if you have exactly one expression. Remember the Python example? Java’s syntax is definitely richer and with a good reason – the λ in Java are late by almost a decade. As an example this is a valid λ – () -> new ArrayList()
  2. If the λ has a single parameter and type of the parameter is automatically inferred the () before the -> operator is optional. This is valid a -> System.out.println(“Got ” + a)
  3. If a λ does not take any parameters then the -> operator needs to be preceded by an empty ().

Example

The most popular example of λ expressions is that of Runnable – and with a good reason. A Runnable has exactly one method – public void run(). The code below shows syntax in Java 8.
       public class ThreadDemo {

           public static void main(String... args) {
               //Java 7 way
               Thread th7 = new Thread(new Runnable() {

                   @Override
                   public void run() {
                       System.out.println("I am the old way");
                   }
               });

               // Java 8 way
               Thread th8 = new Thread(
                       () -> System.out.println("I am the new way")
               );
               th7.start();
               th8.start();
           }

       }
Outputs
I am the old way
I am the new way

No surprises there. Don't forget to note the absence of semi colon in the λ expression. Your code will not compile if you place a ';' at the end. This is one of the main differences between expressions and statements.

Another widely publicized example in the world of λ expressions is that of ActionListener. You add an ActionListener to a widget to tell Swing what to do when certain Action(Event) occurs. This requires encapsulating the code. Traditionally, this was done using Anonymous classes in Java. The anonymous class would be the container for the code to be executed when the Action happened. This kind of containment just got better because you can now use anonymous functions instead of anonymous classes.

1 comment:

  1. can't locate part-2. is there a part 2 at all?

    ReplyDelete