groovy-stream a Lazy Generator class for Groovy

The past few days I’ve been spending my spare time working a Groovy Generator framework that also happens to understand List Comprehensions as well.

It has been pointed out to me that this is not a List Comprehension.  It emulates the style of a List Comprehension, but a List Comprehension is a  specification of a List as a Data Structure, and this is not what this does ;-)

The source code is available from github.

It ended up (after a few iterations) being called a Stream after Brian Goetz’s State of the Lambda post about new features that might make it into Java 8, and in this post, I hope to explain what it does and how to use it in the hope of getting some feedback.

Stream.from — the place to start

The first part of every stream (as of v0.1) is the static from method.  Here are some examples of the core usage of the static from method:

As you can see in the last example, this map form is how list comprehensions are faked.

In every case, you get a Stream returned.  This Stream class (whatever the underlying implementation) implements the StreamInterface, which is basically an Iterator with two additional methods, isExhausted() (which returns true if the Stream has ended) and getStreamIndex() which returns the index of the last element to be returned from the Stream.

All of the following methods add extra functionality to the Stream returned by this method.

Limiting returned elements

To limit the elements returned by a Stream, you can use the where method.

This method takes a closure which returns an Object, and only shows elements where the returned Object is classed as true by Groovy Truth.  The element for consideration is passed into the closure as a single parameter.

With Map based Streams, the map is set as the delegate for the closure, so you can simply do the following:

It is also possible to tell a Stream to STOP running (for example if using an iterator which runs forever that you wish to break out of).  The STOP property is injected as the delegate for the where closure (as a Singleton instance of StreamStopper), and can be used for this purpose as so:

Transforming Results

To change the data your Stream returns, you can use the transform closure.  This takes the current element as a parameter or delegate (in the case of MapStreams as above), and the return value is what is returned from your Stream for this element.  As a simple example, to return a Stream of objects 5 larger than the original Iterable, you can do:

or for maps you could return a summation of the map elements (again as a simple example)

The transform closure is currently the only place where you can modify the using map, which is described in the next section.

The using decorator

The final decoration that can be applied to Streams is the using block.  This is basically a Map of values which are passed as the delegate to all of the closures above (but is modifiable currently only in the transform closure)

As a quick example of using this, let’s create a Stream which has an index on each element it returns:

There are some issues with this behaviour however. One of which is exhibited below:

Looking at the Stream definition above, you would expect it to return 5 items. However, as the transform closure is executed on the element just before it has returned, the idx update is not checked by the where condition until it is too late and 6 elements have been sent back.

Comments?

Hopefully this has explained what you can do with this lump of code. There’s still some way to go to get it rock solid, but I’m hoping people will be able to use this post to try it out, and comment on what is right, what is wrong, and what could be done better…

@tim_yates

 
Blog comments powered by Disqus