Intermediate vs Terminal Operations

Method Chaining in Java and Rails

Photo by JJ Ying on Unsplash

In Functional Programing in Java, https://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467

there is a section I’m beginning to digest on Lazy Streams and the difference between intermediate and terminal operations.

With streams and method chaining, there is an interesting concept of having an infinite series over which you can operate. Since those operations are lazy, they are considered as “intermediate”, and this works without causing your program to blow up because they work together with “terminal” operators.

In order to create an infinite stream, there is a method called `iterate` which takes a starting value and a UnaryOperator as params. (https://docs.oracle.com/javase/8/docs/api/java/util/function/UnaryOperator.html)

Basically it is just a subtype of `Function` where the input and output are of the same type. So you can keep calling it again and again on a value and then its result.

Later on in the chain, when you can call a terminal operation like `.limit` or `findFirst()`, you are changing it to a finite list and all is well.

In Rails

This reminded me of something similar in Rails where Active Record doesn’t query your database until you basically “call a terminal operation”. When ytou actually need to use the data that comes back, that’s when it fires.

For example,

collection = MyModel.where(active: true).where(id: [1,2,3])

Doesn’t get called yet!

Later on…

collection
.where(some_other_attr: some_other_value)
.order(:id)
.select(:name)

You can add more to the query and even still it doesn’t fire. It doesn’t touch the db until you actually use that result such as in:

render json: collection

Which I think is great so you don’t have extraneous requests like we’re always trying to prevent in n+1 scenarios.

Knowing this was a very important part of my understanding how to create complex queries in Rails while still keeping the code simple to read. I think any beginner should have an knowledge of it. If you use that knowledge along with using`.includes` so that all the tables you need are eagerly loaded, you can compose some pretty efficient and elegant queries.

Java Caveat

The only difference between AR and Java Streams is you can only open a Stream once… I thought i’d be able to apply the same principles from Rails into Java but if you want to save one half of your query for later, then you actually have to wrap that in a StreamSupplier like so.

// Save for later
Supplier<Stream<String>> streamSupplier =
() -> { Stream.of("A", "B", "C", "D")
.filter(letter -> // some filter //)
.map(letter -> // some mapping operation //);
}
// Unwrap and reuse it here
Optional<String> result1 = streamSupplier.get().findAny(); System.out.println(result1.get());

So it’s a little more clunky but you can still do it if it is worth it and doesn’t sacrifice readability.

I thinks it’s always great to find similar things across different languages and frameworks. 🤷🏻‍♂