Sunday, 22 February 2015

Software warranty with Java 8

Can we give assurances that a piece of software will run virtually flawlessly for a period of time (say, one year)? Can we give timed warranties in software products, just as other engineering industries have been doing since the dawn of the industrial revolution?

The question seems absurd because, unlike physical parts, software components don’t change with time. Yet, software performance does degrade with time (because its execution environment changes) and more often than not it is exactly time that makes difficult and nasty bugs surface, often with bad consequences.

In absolute sense, it is impossible to guarantee software. Software is way too complex and non-standard to sustain rigorous and legally-binding warranties, let alone that bug-free software is not only a practical impossibility but also a mathematical absurdity1).

Yet, given the right testing methodologies, it is possible to say with enough confidence that a certain software component will run virtually flawlessly for a period of time, under specified conditions. The bugs will be there, some of them will even manifest during the warranty period, but they will be few and far between and their effects will be under control.

I’ll show in this article how to do it, via a simple example. Disclaimer: the illustrative code below is in Java 8. Nevertheless, these principles apply to any language that is capable of generating values lazily - which extends the range to pretty much everything (given the right effort).

Tuesday, 17 February 2015

Rejoice with GS

I always rejoice when I see things well done.

Through the London Java Community channel I’ve learned about the Goldman Sachs Collections library, a state of the art piece of software in active development since 2005.

Goldman Sachs Collection library has reached its 6.0.0 version and it’s been brought in line with Java 8.

The maturity and the level of conceptual consistency this library exhibits is really impressive. Along with Apache Commons, it will be yet another point of reusability that I trust.

If you use collections heavily (and who doesn’t ?), this library is not to miss.

Saturday, 7 February 2015

Enumerators in Java 8: when streams aren’t enough

I decided to write a small library for enumerators in Java 8. What I call an enumerator is, in fact, a special kind of iterator endowed with certain properties such as compose-ability - as Java streams and .NET LINQ operators already have.

I think of enumerators as a specialized type of generators which, in turn, are a watered down version of coroutines.

The intent is obvious: with enumerators, generating potentially infinite sequences of potentially infinite data structures of a potentially unlimited set of topologies defined entirely at runtime becomes a reality. This is a luxury that programmers in high-level functional or logic languages have been enjoying for decades.

Aren’t Java 8 streams enough?

No, they aren’t. Streams are a great addition to Java brought over by Java 8 and they blend wonderfully with Java 8 lambda expressions. Moreover, they have strong parallel support which makes them ideal for exploiting multi-core architectures with minimal effort.

However, there are scenarios where they are either too strong or insufficient:

  • They have been designed with parallelism in mind. While this is a good thing in general, parallelism support makes them quite heavy for very simple tasks where parallelism is not needed.
  • Their parallel nature makes them lack certain operations which are quintessentially sequential:
    • Stream<T>.takeWhile(Predicate<T>) which takes elements while a predicate evaluates to true.

      OBS: we may also call this Stream<T>.limitWhile(Predicate<T>) if we are to stay in line with Stream<T>.limit(int)

    • Stream<T>.zip(Stream<T>) which binds together two streams and produces a stream of pairs
    • Stream<T>.map(Function2<Integer,T,R>) which models Select<T>(this IEnumerable<T>, Func<int,T,R>) from .NET LINQ

  • They lack pipelining which makes them unsuitable for compositions in large number.

    In the case of Stream.concat(Stream<T>, Stream<T>) for example, the JDK documentation warns the user against repeated concatenation as it may lead to large stacks or even stack overflows. This is caused by lack of pipelining, this lack being a result of the parallel nature of streams. I’ll detail on pipelining in a section below.

  • They are not lazy in their arguments. For example, we have Stream.concat(Stream<T>, Stream<T>) but there is no Stream.concat(Supplier<Stream<T>>, Supplier<Stream<T>>). As I explained in a previous post, being supper lazy is sometimes necessary. Java 8 streams, as they are implemented today, are lazy in yielded values but not lazy in input parameters.
  • They are rather hard to customize. Because of their parallel nature, they don’t rely on iterators (which are strictly sequential) but on spliterators (the name is a contraction of split iterators). Spliterators offer support for splitting the iterated sequence to allow parallelism, but customizing them is not the easiest thing to do.