Showing posts with label lambda. Show all posts
Showing posts with label lambda. Show all posts

Monday, December 17, 2012

Java 8 vs. Xtend

As you might know Java 8 will finally bring an important new feature to the Java programming language :
Lambda Expressions, a feature already supported by many other languages, such as Xtend

Xtend is a statically typed JVM language that translates to readable Java source code. It's very different to other JVM languages in the sense that aims to be a better Java by better supporting existing Java idioms and fully supporting the existing ecoystem. Xtend doesn't force you to rewrite your existing Java apps or drop your beloved and well-proven frameworks, but instead allows for using existing Java APIs in a much nicer way without any interoperability issues. In contrast to e.g. Scala or Clojure it is more like an extension to Java than a replacement.

Identifying and supporting existing Java idioms is not too hard given the huge existing code base but what about the future? Will Xtend also be a better match for the upcoming Java 8 libraries which were explicitly defined for the new Java 8 lambda syntax?

To find out I simply looked at Brian Goetz’s latest version of State of the Lambda : Libraries Edition and translated the contained Java code examples to Xtend.

In the following you see the code for Java as written by Brian and the translation to Xtend. Both are using the exact same Java 8 APIs and contain the same amount of static type information!

Example 1

Java 8:
shapes.forEach(s -> { s.setColor(RED); });
Xtend:
shapes.forEach[color = RED]

This first example already reveals most of the important differences.

First the general syntax for a lambda expression in Java is using an arrow (->), while Xtend uses squared brackets (like smalltalk). Also in Xtend a lambda expression doesn't need to be passed using braces. Although you could write shapes.forEach([color = RED]) you don't need to.

In Java 8 the body of a lambda is either a block or a single expression. So whenever you need to use a statement you have to add the curly braces and semicolons. In Xtend everything is an expression and the lambda itself is already a block expression.

In Xtend you can omit the declaration of a parameter name. In that case the parameter is called 'it' and is implicit (similarly to 'this'). If you want to give it a name you do it like this : shapes.forEach[ s| s.color = RED]

Unrelated to Lambdas but interesting in this case: Xtend allows to use assignments to call setter methods.

Example 2

Java 8:
shapes.stream()
      .filter(s -> s.getColor() == BLUE)
      .forEach(s -> { s.setColor(RED); });
Xtend:
shapes.stream
      .filter[color == BLUE]
      .forEach[color = RED]

Not too much new stuff in here. Xtend lets you omit empty parenthesis and you can call a getter using the property's name.

Example 3

Java 8:
List<Shape> blue = 
    shapes.stream()
          .filter(s -> s.getColor() == BLUE)
          .into(new ArrayList<>());
Xtend:
val blue = 
    shapes.stream
          .filter[color == BLUE]
          .into(new ArrayList)

Here we see type inference in action. While you can use the diamond operator in Java, you can leave it out in Xtend. Also the variable doesn't need to be explicitly typed as the type can be fully inferred from the right hand side.

Example 4

Java 8:
Set<Box> hasBlueShape = 
    shapes.stream()
          .filter(s -> s.getColor() == BLUE)
          .map(s -> s.getContainingBox())
          .into(new HashSet<>());
Xtend:
val hasBlueShape = 
    shapes.stream
          .filter[color == BLUE]
          .map[containingBox]
          .into(new HashSet)

Note how readable the lambdas get when you can leave out all the cryptic clutter. Let's compare a last example from a later section of the document:

Example 5

Java 8:
List<Album> sortedFavs =
    albums.stream()
          .filter(a -> a.tracks.anyMatch(t -> (t.rating >= 4)))
          .sorted(comparing(a -> a.name))
          .into(new ArrayList<>());
Xtend:
val sortedFavs =
    albums.stream
          .filter[tracks.anyMatch[rating >= 4]]
          .sorted(comparing[name])
          .into(new ArrayList)

Although the libraries as well as the examples have been written for Java 8, the Xtend code is still much less cluttered with symbols and therefore significantly more readable. Yet the type information is exactly the same! Xtend is just a bit smarter with type inference and is syntactically less rigid.

Besides that and the fact the Xtend compiles to readable Java 5 code, it has many other important features to offer.

Friday, February 17, 2012

Lambdas and functional interfaces in Java 8

Lambda's in Java 8 are going to be very nice. I like how they integrate with the existing type system. They automatically convert to so called interface types (previously SAM types), which are interfaces where only one method needs to be implemented. That allows to just use e.g. Google Guava right away without any modifications.

Also this means that there are no types like Scala's Function23 and you even can have lambdas with 24 arguments (not saying that this is a particularly good idea :-)).

The most commonly used functional interfaces will likely come with java.util.* since they are needed there. So hopefully we won't be forced to redeclare the obvious functional interfaces all over the place. I consider such a commonly used set of generic interface types important since otherwise we will end up with redefining them in every project.


The downside of not having explicit "function types" is that writing signatures will still be quite clumsy.

To declare a higher-order method like e.g. 'map' on java.lang.Iterable, in Java you would have to invent some functional interface and then write :

public <R> Iterable<R> map(Function<? super T, ? extends R> predicate)

Compared to Xtend, which also supports the conversion to functional interfaces, there is a special type signature for functions, which will get the generics (i.e. upper and lower bounds) straight for you. So in Xtend you can just write the following instead :

def <R> Iterable<R>  map( (T)=>R predicate )

(T)=>R really is just a short-form of Function<? super T, ? extends R>

Using this type signature in casts also allows for easily convert from one functional interface to another. Also you can declare lambdas without any context type information. In Java 8 the following will not be possible, since the context type (java.lang.Object) is not an interface type:

Object myFunction = (String s)-> s.toUpperCase();

So instead you'd write:

Function<? super String,? extends String> myFunction = s -> s.toUpperCase()

In Xtend you can instead just use type inference and write :

val myFunction = [String s| s.toUpperCase()]

So as you can see having a syntactical short form for type signatures like Xtend would improve readability of signatures a lot and would also help developers getting the upper- and lower bounds right. But even without this using auto conversion to interface types is a great way to add lambdas to Java.