Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy evaluation of chained functional methods in Groovy

What I've seen in Java

Java 8 allows lazy evaluation of chained functions in order to avoid performance penalties.

For instance, I can have a list of values and process it like this:

someList.stream()
        .filter( v -> v > 0)
        .map( v -> v * 4)
        .filter( v -> v < 100)
        .findFirst();

I pass a number of closures to the methods called on a stream to process the values in a collection and then only grab the first one.

This looks as if the code had to iterate over the entire collection, filter it, then iterate over the entire result and apply some logic, then filter the whole result again and finally grab just a single element.

In reality, the compiler handles this in a smarter way and optimizes the number of iterations required.

This is possible because no actual processing is done until findFirst is called. This way the compiler knows what I want to achieve and it can figure out how to do it in an efficient manner.

Take a look at this video of a presentation by Venkat Subramaniam for a longer explanation.

What I'd like to do in Groovy

While answering a question about Groovy here on StackOverflow I figured out a way to perform the task the OP was trying to achieve in a more readable manner. I refrained from suggesting it because it meant a performance decrease.

Here's the example:

collectionOfSomeStrings.inject([]) { list, conf ->  if (conf.contains('homepage')) { list } else { list << conf.trim() } }

Semantically, this could be rewritten as

collectionOfSomeStrings.grep{ !it.contains('homepage')}.collect{ it.trim() }

I find it easier to understand but the readability comes at a price. This code requires a pass of the original collection and another iteration over the result of grep. This is less than ideal.

It doesn't look like the GDK's grep, collect and findAll methods are lazily evaluated like the methods in Java 8's streams API. Is there any way to have them behave like this? Is there any alternative library in Groovy that I could use?

I imagine it might be possible to use Java 8 somehow in Groovy and have this functionality. I'd welcome an explanation on the details but ideally, I'd like to be able to do that with older versions of Java.

I found a way to combine closures but it's not really what I want to do. I'd like to chain not only closures themselves but also the functions I pass them to.

Googling for Groovy and Streams mostly yields I/O related results. I haven't found anything of interest by searching for lazy evaluation, functional and Groovy as well.

like image 647
toniedzwiedz Avatar asked Nov 19 '14 18:11

toniedzwiedz


1 Answers

Adding the suggestion as an answer taking cfrick's comment as an example:

@Grab( 'com.bloidonia:groovy-stream:0.8.1' )
import groovy.stream.Stream

List integers = [ -1, 1, 2, 3, 4 ]

//.first() or .last() whatever is needed
Stream.from integers filter{ it > 0 } map{ it * 4 } filter{ it < 15 }.collect()

Tim, I still know what you did few summers ago. ;-)

like image 171
dmahapatro Avatar answered Oct 25 '22 14:10

dmahapatro