Jun 062013
 

In this final part of the series I am going to look into the Lambda Expressions pattern to define an internal DSL for constructing object graphs. I am also going to provide some conclusions for the three patterns.

Lambda Expressions are all the rage these days. The wave started to rise with the paradigm shift towards multithreaded applications. It’s arguably simpler to create a thread-safe application if it’s solely composed of pure functions (i.e. methods without side effects) than the traditional way of composing and sharing potentially mutable objects. Although you can certainly write functional code in a non-functional language like Java 7, it gets a lot easier with Lambda Expressions in Java 8.

Let’s look at the following example code from Mohamed Sanaulla’s excellent blog posting again:

Graph(g -> {
  g.edge( e -> {
    e.from("a");
    e.to("b");
    e.weight(12.3);
  });
  g.edge( e -> {
    e.from("b");
    e.to("c");
    e.weight(10.5);
  });
});

First, the good parts: Like with the Method Chaining pattern (if done right), the order of statements is irrelevant: You can swap them at will and it will still construct virtually the same object graph.

Another advantage is that you can provide some context with configurable default values for some properties of the constructed object graph. Consider this example:

public class GraphBuilder {

    double defaultWeight = 1.0;

    ...

    public void edge(Consumer<EdgeBuilder> eConsumer){
        EdgeBuilder eBuilder = new EdgeBuilder();
        eBuilder.weight(defaultWeight); // configure default value
        eConsumer.accept(eBuilder);
        ...
    }
}

In this modified example from Mohamed Sanaulla, I have added the eBuilder.weight(defaultWeight) statement. The effect is that if the eConsumer does not call the weight(Double w) method, then defaultWeight will prevail. Note that because this is an instance field, I do not need to share mutable state with other threads (I am assuming that there is a getter and a setter method for defaultWeight).

As you can see, the Lambda Expressions pattern is pretty much on par with the Method Chaining pattern. My biggest grief about it is that it’s a lot more code to write and read. Compare the initial example to the equivalent expression with Method Chaining again (I am using my Builder and Injection interfaces):

Graph()
  .edge()
    .from("a")
    .to("b")
    .weight(12.3)
    .inject()
  .edge()
    .from("b")
    .to("c")
    .weight(10.5)
    .inject()
  .build();

This code has the same number of lines (twelve), yet it doesn’t need the ugly function and method call notation.

Conclusions

Finally, let’s compare the three patterns Method Chaining, Nested Functions and Lambda Expressions for defining an internal DSL for constructing object graphs: Among the three, the Nested Functions pattern clearly wins the competition for conciseness. As a reminder, here is the equivalent expression using the Nested Functions pattern to construct the object graph:

Graph(
  edge(from("a"), to("b"), weight(12.3),
  edge(from("b"), to("c"), weight(10.5)
);

This is just four lines of code instead of twelve – hooray! However, the Nested Functions pattern has two serious disadvantages: First, it can’t disambiguate parameters of the same type, so it can’t help you to avoid mistakes in their ordering. Second, it can’t easily provide some configurable context to the construction procedure without sharing mutable state. Considering the shift of paradigm to multithreaded applications, the last point is probably the most important. Therefore, I would only choose this pattern if these points do not apply and I could be confident that they still don’t apply when considering foreseeable changes in future versions of my code.

The Method Chaining and Lambda Expressions pattern do much better in this respect. Though they are functionally equivalent, the Method Chaining pattern is more concise. And then there is another difference: The Method Chaining pattern naturally constrains you to do only object graph construction (anything else will not compile) and the IDE can easily guide you through this procedure with code completion. In contrast, there are no such constraints with the Lamba Expressions pattern. You may think this is an advantage of the Lambda Expressions pattern, but I consider this being a disadvantage: My argument is that the purpose is object construction here and anything else should be inhibited (e.g. compiling your claim for tax refunds).

So the winner is… (drum roll and fanfare): The Method Chaining pattern! (your mileage may vary)

  One Response to “On Domain Specific Languages in Java: Lambda Expressions”

  1. The Lambda expression approach uses the Function sequence technique for creating DSL where in we line up a series of function calls to build the DSL.

Sorry, the comment form is closed at this time.