Rack of Lambdas

nvisia is an award-winning software development partner driving competitive edge for clients.

JavaLambda.pngSo, over the last few months I've been hard at play with a personal project. This has given me latitude to fool around with various things I find interesting (more to come) the first of which was to upgrade to Eclipse 4.5 and Java 8 (jdk 1.8). The main driver behind this was to fool around with Lambdas in Java, since I'd been using the C# equivalent by day. A little thought (well, actually, just paying attention to myself saying to myself "Self, this is pretty annoying") provided me with a couple of opportunities to use this language feature.

What is it?

So, what's a lambda? For practical purposes, it's a bit of code that can be defined (somewhat) dynamically and treated as an object. You can assign it to a variable, use it in an expression-like way and pass it as a parameter.

The term lambda comes from the lambda calculus, one of the historical underpinnings of computing. The lambda calculus stands alongside the Turing machine as mathematical models of computability (the two were shown by Turing to be equivalent) Note that the Java syntax alludes to the lambda calculus more than it implements it.

The Good

The first place I found to use lambdas was in various scenarios involving iteration of collections. Java supports the 'map/reduce' paradigm through a new 'stream' feature in the collections framework. The 'streams' abstract processing of sequences of arbitrary objects in a way that's both similar to various industry strength tools and parallelizable. The stream methods, where appropriate, return streams of appropriate types, so the calls can be chained together. They're also mostly generic, so what gets returned is typesafe as far as the compiler goes, and your IDE can mostly help you with the usual code assistants (actually remembering anything is very much yester-decade at this point).

What's it look like? Well, where I used to do things like this:

BigDecimal total=BigDecimal.ZERO; 
for (InventoryEntry entry : itemInventory) { 
    total = total.add(entry.getQuantity()); 
} 
I now have this:
totalInventory = itemInventory.stream().
map(entry -> entry.getQuantity()).
reduce(new BigDecimal(0), (left, right) -> left.add(right)); 

This is probably a bad first example, since there's a bit going on: there's two lambda expressions, plus the map/reduce paradigm. So what's going on here?

Well, I have a collection, so I call 'stream()' to get the typed stream from it, then call 'map' on that stream. The 'map' method takes a lambda that takes the stream's element type as a parameter and returns another type - a lambda that literally maps each element of the collection to another type. In this case, I'm just returning a member of the element type, but you could do pretty much anything in there - look-ups, transformations, etc.

Map returns a stream of the lambda's return type, so in this case Stream. I call 'reduce' on that, passing in a starting value (AKA 'identity element' for that class), and a lambda that takes two of the stream element type (BigDecimal here) and returns the same type. Reduce calls the lambda with the identity and the first element, then takes the result and calls the lambda with it and the second stream element, etc. Thus, 'reduce' reduces the stream down to a single object representing the entirety of the stream. In this case it sums the stream, but it could be an average, a checksum, or some more esoteric operation.

Now, why did I include that parenthetical about 'identity element'? Well, suppose I have a couple million of these things and I want to hurry my summation along. I'd want to put all my cores to use, right? That's where 'parallelStream()' on Java's Collection class comes in. It returns a 'possibly parallel Stream', meaning my lambda will be getting called in parallel in multiple threads, then again to reduce those reduced subsets. In order for all that to work, the object you pass in as the 'initial' has to be an identity element in respect to the lambda you pass in. In other words both identity γ X = X and X γ identity = X, (where γ is your lambda) must hold true. (I'm always happy when I can say 'identity element for that class' and mean it both mathematically and programmatically).

One last note: Java 8 also added method references. Referring to a method using the 'classname::method' allows us to use certain methods in the place of lambdas. As long as the method's signature matches the expectations of the lambda ('is assignable to the functional interface in question' is the real test - more on that later), we can use it in the place of the lambda. So, my real code is this:

totalInventory = itemInventory.stream().
map(entry -> entry.getQuantity()).
reduce(new BigDecimal(0), BigDecimal::add); 

Note that, for non-statics, the method's 'this' pointer counts as the first parameter, so BigDecimal::add is (BigDecimal, BigDecimal) -> BigDecimal, in a vaguely Haskell-ish type notation (probably just similar enough to be confusing). Really, for this to work properly, the method in question should have no side effects (as required in lambda calculus itself as well as 'pure' functional languages)

So, what if I want to accept a lambda as a parameter?

So, another aspect of this personal project is a UI written using the Eclipse RCP framework and the SWT toolkit. A big chunk of this has been writing repetitious glue code (LabelProviders and EditingSupports). Wanting to play with my newfound toy, I wrote 'Functional' versions of those (although whether this really constitutes functional programming is doubtful. Nope, not even debatable, just doubtful, and that's being nice).

Now we get a bit under the hood. Java's lambda support is based in the notion of 'Functional Interfaces' - any interface with one and only one abstract method is a functional interface. They can be annotated with @FunctionalInterface in order to enforce this. Java 8 has a bunch of such interfaces already defined with type parameters for the return value and method parameters - see java.util.function. In my case, I mostly needed functions with one or two parameters, so I mostly used Function<t, r=""> and BiFunction<t,u,r>. Here's an example of how this is declared:

public FunctionalColumnLabelProvider(Function<object, string=""> getText) { 
    this.getTextImpl = getText; 
} 

This is the constructor for my 'functional' LabelProvider adapter for use in various trees and tables JFaces widgets. Really, it's like any other object. So how is the lambda called? Well, you just call the interface that gives the lambda its signature: return getTextImpl.apply(element); Kind of a let-down, actually: all that new stuff to create lambdas, and there's no syntactic sugar on the calling side? No 'getTextImpl(element)' so that I can continue the delusion that this isn't just an anonymous inner class under the hood? Ah, well.

Just for conversation's sake, here's an example of me passing lambdas into one of these not-at-all-functional JFace adapters of mine:

Function<object, string=""> getProductQuantity = (Object o) -> { 
    Production prod = (Production)o; 
    if (prod.getQuantity()!=null) { 
       return currencyFormat.format(prod.getQuantity());  
    } else { 
       return "0.00"; 
    } 
}; 
TableViewerColumn productQuantityColumnViewer =  

new TableViewerColumn(productTableViewer, SWT.NONE); 
productQuantityColumnViewer.setLabelProvider( 
    new FunctionalColumnLabelProvider( getProductQuantity ) ); 
productQuantityColumnViewer.setEditingSupport( 
    new FunctionalEditingSupport(productTableViewer,  
        getProductQuantity,  
        (Object elem, Object val)-> { 
             BigDecimal value = new BigDecimal((String)val); 
             ((Production)elem).setQuantity(value); 
    } )); 

So this shows a couple of things:

  1. I define the lambda and assign it to a variable (getProductQuantity) typed as the functional interface, then use it a couple of times. If I were doing this again, I might just make that a member method and refer to it using the :: notation.
  2. Java lambdas are free to use identifiers from the scope they're defined in. Note that locals have to be 'effectively final' for this to work.
  3. For this usage, it's maybe not much better than anonymous inner classes. The code gets just as nested, and while you save a few lines of content-free noise, it's not a huge readability improvement. Moving to what I described in point one (the lambdas being replaced by member methods) has it feeling a lot like my old X Windows days: is a method reference that much different than a function pointer? (well, yes, because of the whole closure thing, and having your this pointer auto-thunked in and all. But it's close enough to give me deja vú)

In Closing

Now what? Well, let's consider when you'd want to use these things. In my opinion, the use cases around collections make quite a bit of sense - you're able to be terse and to the point. The nice things there are that you're able (generally) to be fairly close to 'real' functional programming for that brief moment - you write a bit of code that has an input and an output with no side-effects, and the framework allows you to compose those bits with quite a lot of leverage. This makes it a very good way to get into the functional way of thinking, to boot. Interestingly, this is the case where you don't actually save much in the way of typing - a for loop vs. a map/reduce is a horse apiece as far as keystrokes go. Part of this is down to the decision the Java maintainers made to introduce map/reduce via streams, because you can take advantage of multiple cores, but even if those weren't there it wouldn't be much less typing.

On the other hand, the business of squeezing out a couple of lines of annoying boiler-plate in the GUI code, I'm not so fond of. It's basically a slightly improved syntax for what I've been doing for a decade or more, but doesn't really provide much more or really change the paradigm. I'll keep going in that direction, for sure, but I'm not going to claim it's revolutionary or anything other than slightly less irritating.

In a nutshell, lambdas in object oriented languages give us another tool in our box. As we get more accustomed to these tools we'll be able to leverage them better, doing more with less code in the areas where they're helpful. They don't make our object oriented languages into functional languages, though - they allow (and even encourage) the sorts of side-effects that functional programming frowns on and have to interact with object oriented libraries and frameworks that have been built over the last 2 or 3 decades. For real functional, I'm looking at Scala (and probably Haskell, for its maturity and purity).

Related: Lambda Quick Start

Related Articles