Getting Real with Enumerable.java

Two weeks back I posted a video explaining how to get started with Enumerable.java. While this might have been enough information to start playing, there is a lot more to consider when going with Enumerable.java for production use.

Over the past few weeks I went through that experience on my project and here are some of the practices that we found useful. Håkan also released version 0.3.0 that incorporates some of our learnings.

When working in the IDE we used the javaagent to weave the lambdas. However there were a few caveats. In version 0.2.4 you could only black list packages to prevent them from being weaved. In most cases it is easier to specify explicitly for which package to enable weaving. Also when using contemporary java goodness there is other parties doing byte code magic. We experienced these problems with spring’s scoped proxies, therefore we introduced another parameter to filter out classes by a regular expression applied to the full classname. Proxy classes usually come with a lot of dollar signs in their names.
So the vmargs we are using in development are:

-javaagent:lib/deploy/enumerable-agent-0.x.x.jar  
-Dlambda.weaving.included.packages=felix.application 
-Dlambda.weaving.exclude.pattern=\$\$

If you are using eclipse you can specify these parameters as default vm-parameters in your JRE definition:

While using an agent in the ide is acceptable it gets messy, when deploying the application. Essentially all the JVMs on the way to production including your application server would need the agent. What we did instead is, we instrumented our class files in our build using the AOT lambda compiler. So this is a single step after the compilation. If you are using ant it could look somewhat like this:

<target name="lambda" depends="compile">
        <java fork="true"
              classname="lambda.weaving.LambdaCompiler"
              failonerror="true">
 
              <sysproperty key="lambda.weaving.debug" value="true"></sysproperty>
                  <classpath>
                      <path refid="prod"/>
                  </classpath>
              <arg value="prod.jar"></arg>
        </java>
    </target>

Important: The lambda weaver needs debug information on local variables and also you need to encode your source files as utf-8 to get the fancy lambda letter, so perhaps you have to change your compile target like this:

<javac srcdir="src/java" 
            destdir="${basedir}/tempCompile" 
            classpathref="prod"
            encoding="utf8"
            debug="on"
            debuglevel="lines,vars,source"
/>

From a technical point of view this is all you need to do. However there is also the question of how to use the new feature “responsibly”. In our code base we used the generic CollectionUtils to filter and transform Collections. While they are elegant from a theoretical point of view, they are quite an insult to the eye, so our aim was to replace all these with Enumerable.java. Functional operations on collections are very well understood (the method names used in Enumerable.java go at least back to Smalltalk-80, that’s thirty years).

So the advice introduce your team the following methods and aim to restrict the use of lambda expressions to those:

  • select
  • collect
  • find
  • inject
  • groupBy
  • While there is remarkable support for arrays and primitives, try to stick to
    Collections and proper Objects.

I think these alone justify the investment.
Things you should avoid or do only in after proper consideration:

  • Do not declare parameters of function types
  • Do not declare local variables of function types
  • Do not declare fields of function types
  • Do not modify enclosed state from within the closure, i.e. local variables or fields
  • Generally avoid side effects

The typical transformation we did, looked something like this:

    private Set<Order.Status> retrieveOrderStatuses() {
        Set<Order.Status> statuses = new HashSet<Order.Status>();
        if (orders == null) return statuses;
 
        for (Order  order : orders) {
            statuses.add(order.getStatus());
        }
        return statuses;
    }

becomes:

    @LambdaParameter private static Order o;
    private Set<Order.Status> retrieveOrderStatuses() {
        if (orders == null) return emptySet();
        return  collect(orders, ?(o, o.getStatus())).toSet();
    }

Because getting the lambda character (you might also want to use the alias fn) is a bit tricky you might want to use this java editor template for eclipse that also sorts out the static import:

?(${impst:importStatic('lambda.Lambda.*')}${cursor})

Sticking to these simple rules actually helped us getting away from the anonymous inner classes and CollectionUtils and that was an easy sell to the mostly mathematically inclined team. In my opinion the fact that lambdas are restricted to expressions is actually a good thing.


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.