August 9, 2015

LambdaOmega: Java Collections, Lambdas & Promises simplified



Update June 12, 2016: LambdaOmega 0.3 is now officially released. Read the update post here.

Instead of a lengthy introduction, please read what LambdaOmega adds to a Java 8-based project:
  • Simple literal-like instantiation of Lists, Maps, and Ranges.
Map<String, Integer> map = m("a", 0).a("b", 1).a("c", 2);
  • Collection methods which feel familiar, but are far more powerful and concise.
List<Integer> zeroToSix = l(0, 1, 2).a(3, 4).addAll(l(5, 6));
  • A simple Lambda-driven API functional interface for straightforward application of the most widely used Collection functions, inspired by JavaScript lodash and Groovy.
Integer negative = l(1, 2, -1, 3).find(it -> it < 0);
  • A grand unified Collection API where a List is also a Map from int to T, and a Map is also a List of entries, and even a Function is also a Map.
L<String> xy = l("a", "b").PutAll(m(0, "x").put(1, "y"));
  • Convert everything into everything within Collections and Functions.
Set<Integer> set = l(0, 1, 2).toSet();
  • Promise: A drop-in replacement for CompletableFuture with a superior, more concise API.
new Promise<String>().completed(
    (it, err) -> {if (err == null) {return "success: " + it;} else throw new RuntimeException();}
);
  • A simple 2D vector (2-ary tuple) class.
V2<String, Integer> vector = v("a", 0);
  • Plus many helper functions and extras, as e.g. inspired by the Groovy language.
println("Hello Omega");
  • Small footprint (<75KB), no other dependencies.
  • Thoroughly tested.
  • Human-readable documentation.
Please head over to the GitHub repository to learn more about what LambdaOmega provides and how to use it.

Read on if you’re interested in its back-story.

Why would I use a 3rd party Collection API?

I’m a Java developer. I know that Java, both as a language and as a platform, has many strong parts. A concise Collection API is not one of them. When compared with other, more modern languages such as Groovy, working with the Java Collection API means bloated, redundant, potentially copy-pasted code.

Actually, quite a few Java core APIs are considered not good enough by some people; this has given birth to various 3rd party libraries such as Apache commons or Joda time.

I do firmly believe that concise code is a tremendous advantage in terms of productivity, readability and maintainability. Want to make less errors? Write less code. This is especially true for everyday / every minute tasks such as Collection manipulation. Coming back from Groovy, one has to admit that the Java Collection API, including their newest Lambda additions really is bloated and inconsistent.

The motivation behind OmegaLambda

On this blog, I recently complained a lot about the state of Java’s lambda API, especially in regard to Collections and the CompletableFuture / lambda API. But making complaints is not constructive. Making a change is far more constructive.

As briefly mentioned, the OmegaLambda library is primarily inspired by:
  • Groovy, which shows that it’s absolutely possible to make even a complex Collection API both more simple and more powerful at the same time.
  • lodsh, which shows that, at least in the JavaScript world, there actually is reason and acceptance to use a 3rd party wrapper API even for something as basic as Collection manipulation.

Oh, the library name has no further significance. I just couldn’t think of anything better than LambdaOmega.

So… is this a Guava clone?

No. Guava primarily serves two purposes:
  • providing Java 8-like stuff for projects stuck with pre-Java 8;
  • providing additional (Collection) utilities and data structures which are not present in vanilla Java.
This is not at all what LambdaOmega offers. Instead, this project provides simple wrappers for existing Java 8 functionality to make their usage more straightforward and enjoyable. Also, Guava has a much bigger footprint.

What about performance?

  • LambdaOmega doesn’t make use of reflection. It really just wraps Collection API method calls and should perform similarly to vanilla API calls.
  • However, intermediate operation on LambdaOmega collections will always create a new Collection much unlike vanilla Java collections which just operate on a stream. This may decrease performance for big collections.
Thus, even though LambdaOmega should perform OK in everyday situations, keep in mind that it is not and will never be built primarily for speed.

Again: Why would I use this?

Use LambdaOmega where you make intense use of Java’s collection API and keeping your code clean and concise is key.

One perfect usage scenario for LambdaOmega of course are JUnit tests which typically invoke lot of collection boilerplate code.

As a drop-in-replacement for CompletableFuture

Having dissected the CompletableFuture API of Java 8 in the previous blog post, I have located many inconsistencies in that API. As CompletableFuture promises make intense use of lambda expressions, I have included a drop-in-replacement for CompletableFuture named Promise in the LambdaOmega library.

This will turn the bloated, inconcise Java CompletableFuture API:
  intermediary terminal terminal (Runnable) A E
Construct from task static supplyAsync   static runAsync   X
complete listener thenApply thenAccept thenRun X X
completeExceptionally listener exceptionally        
Combined complete and completeExceptionally listener handle whenComplete   X X
Dynamic callback chaining thenCompose     X X
AND all combining static allOf        
AND both combining thenCombine thenAcceptBoth runAfterBoth X X
OR any combining static anyOf        
OR either combining applyToEither acceptEither runAfterEither X X

into the much cleaner Promise API:
  intermediary terminal terminal (Runnable) A* E
Construct from task static completeAsync   static completeAsync   X
complete listener completed completed completed X X
completeExceptionally listener completedExceptionally        
Combined complete and completeExceptionally listener completed completed   X X
Dynamic callback chaining then     X X
AND all combining static allOf**        
AND both combining and and and X X
OR any combining static anyOf**        
OR either combining or or or X X

*Synchronous / asynchronous is controlled by a boolean flag rather than by the method name.
**The flawed implementations of allOf / anyOf have been fixed.

Project status and future plans

The current status of the library is unstable / experimental / request for feedback.

Even though its core functionality is perfectly working already, there’s no stable release yet. I decided to share it on my blog nonetheless as I’m hoping to receive feedback before fixing the API.

Please let me know either in the comments section or over on GitHub what you think about this library. Do you think it is useful, useless, harmful, crazy, funny? I’m open to any kind of improvement proposals.

Current to-dos are:
  • Complete test coverage for indexed access on L and sequential access on M
  • Complete F (function) implementation
  • Add more Groovy-like collection functionality (http://docs.groovy-lang.org/latest/html/groovy-jdk/java/util/Collection.html)
  • Add more conversion methods
  • Properly implement S (Set).
I’ve started this project for my own pleasure and usage and to learn more about Java 8’s collection API. I will try to work on a complete, stable implementation in the near future, but I may as well leave it as it is for now. If you’re really enthusiastic about this library and you’d like to team up to get it finished, please let me know.

Update June 12, 2016: LambdaOmega 0.3 is now officially released. Read the update post here.

No comments:

Post a Comment