Is elegant Object Oriented Programming Functional Programming?

Danilo Oliveira
Jun 14, 2018 · 5 min read

Recently, I stumbled upon a curious group of programmers on Internet. They claim to know the “true Object Programming”, and condemn some practices normally seen as okay in the world of OOP. The main laws of true OOP can be summarized as:

  1. Not using NULL references;
  2. Not using static methods;
  3. Using only immutable classes;
  4. Not using instanceof, performing type casting, or using reflection.

There are many other laws such as: do not put multiple return statements in a function [1], refactor a method if you see a blank line [2], only assignments are allowed inside constructors [3], get rid of all getters [4], and many others. I think those additional OOP laws are quite debatable, but my aim in this post is not judging them.

The leader of this group is called Yegor Bugayenko, and he is quite famous up to now. He already published two books on object oriented programming (Elegant Objects 1 & 2), beside giving talks in some conferences. Unfortunately, I don’t own the books yet, but we can learn a lot about his ideas on his personal website. It is quite apparent I cannot judge the entire EO methodology as I did not read his books, but I would like to write about his strongly opinionated ideas from his publicly available posts.

In this post, I will examine his “Stream vs Decorators” article: https://www.yegor256.com/2017/10/10/streams-vs-decorators.html

In this article, he makes a case against Java 8 Stream API. First, he introduces a problem he wants to solve. Then, he exposes three solutions: a procedural solution, a solution based on the stream API, and finally his EO solution.

Lets take a look at the Java 8 solution:

AtomicInteger index = new AtomicInteger();StreamSupport.stream(probes.spliterator(), false)
.filter(probe -> probe != 0.0d && probe != 1.0d)
.limit(10L)
.forEach(
probe -> System.out.printf(
"Probe #%d: %f",
index.getAndIncrement(),
probe * 100.0d
)
);

The AtomicIndex workaround is a serious flaw indeed. We cannot blame him for giving a defective solution for proving his point, as this solution is also found in some answers in this StackOverflow thread:

https://stackoverflow.com/questions/18552005/is-there-a-concise-way-to-iterate-over-a-stream-with-indices-in-java-8

Yegor states:

“(…) First, see how easily we got into trouble when we didn’t find the right method in the Stream interface.”

He is completely right. The Stream<T> interface is so damn difficult to extend. This is an option only experts would choose. Extending an interface is fulfilling a contract, and it is very hard to accomplish this when the interface has such huge amount of methods. Sadly, the Stream API version 8 lacks the zip function so we could process the elements of a stream paired with indexes.

The solution I found for overcoming this issue was creating an Iterator and converting it to a stream via the utility method StreamSuport.stream . This is my solution:

https://gist.github.com/danilomo/999c9d209e97218fe58c8a9042ca1261

While trying to implement this solution, I found the following issues:

  • Java does not have a tuple API, so I end up using javafx.util.Pair. This sucks so hard. The JDK overlords supposedly wanted to prevent us from overusing pairs instead of relying on proper abstractions [5]. I understand the good intentions behind this, but after introducing support for functional programming, tuples are needed;
  • Besides zip, Java 8 also lacks dropwhile and takewhile, what is a shame. What is the point of having potentially infinite lazy lists without having takewhile?
  • Stream.generate and Stream.iterate are nice methods for generating infinite streams, but what if I want to stop generating items at some point? Java 8 does not allow this. Java 9 introduced an additional iterate implementation which takes a Predicate object. In Java 9 you cannot stop generate , but you can call generate(gen).takewhile(pred)for this purpose.

We can save our zip utility static method in a library for overcoming its absence in the standard API. In the mentioned StackOverflow thread, many libraries were suggested: Guava, jOOL, etc.

Now lets take a look at Yegor’s solution:

new AndWithIndex(
new Mapped<Double, Func<Integer, Boolean>>(
new Limited<Double>(
new Filtered<Double>(
probes,
probe -> probe != 0.0d && probe != 1.0d
),
10
),
probe -> index -> {
System.out.printf(
"Probe #%d: %f", index, probe * 100.0d
);
return true;
}
)
).value();

It uses his Cactoos API (https://github.com/yegor256/cactoos) primitives and it heavily relies on decorators. Nothing is done until the value() method is called, so it behaves just the Stream API. It is more flexible than Java 8’s fluent interface approach.

My point in this post is that Yegor’s solution is quite close to functional programming. For sake of comparison, lets examine the equivalent Haskell code:

zip [1..] (map (* 100.0) (filter (\x -> not (x == 1.0 || x == 0.0)) (take 10 probes)))

Due Haskell’s non-strict semantics, the list is traversed only once, and we can compose as many zip/filter/map/takewhile/dropwhile/etc. as we want. In Yegor’s solution, each decorator layer express an intention of transforming some input, but it does not touch the actual data structures until it is necessary. This is exactly how Haskell expressions behave in its lazy evaluation semantics. In others words, those Cactoos objects are similar to thunks in Haskell. They express a function application, and capture the parameters to be used, but they do not evaluate immediately. To conclude this text, one more convincing example:

List<String> s = Arrays.asList("1", "2", "3", "4", "5");Filtered<Integer> f = new Filtered<Integer>((x) -> x % 2 == 1,
new Mapped<String, Integer>(
(x) -> Integer.parseInt(x),
s.iterator()));

, and the Haskell equivalent:

let s = ["1", "2", "3"] in filter odd (map read s)

The “Mapped” object behaves exactly like the “map read s” expression. Haskell will not, by default, immediately apply the mapping. The “Mapped” object behaves the same, since Cactoos was also designed with lazy evaluation in mind.

Final remarks

If EO is functional programming, why not using a functional language instead? I praise Yegor for his originality, but I also follow the mottos “practicality beats purity”, and “in Rome, do as the Romans do”. Java is an imperative language. Trying to stick to a pure declarative solution may lead to a code which is cumbersome to write and read. When programming in Java, I absolutely do not care using imperative programming when it is more convenient. Java does not favor declarative programming, and often it gets in the way when we try to program in this style.

Nevertheless, the problems of Java 8's fluent interface design are evident. Not offering tuples/takewhile/dropwhile/zip/etc. is inexcusable for an API which claims to be functional/declarative.

_______________

References:

[1] https://www.yegor256.com/2015/08/18/multiple-return-statements-in-oop.html

[2] https://www.yegor256.com/2014/11/03/empty-line-code-smell.html

[3] https://www.yegor256.com/2015/05/07/ctors-must-be-code-free.html

[4] https://www.yegor256.com/2016/04/05/printers-instead-of-getters.html

[5] http://mail.openjdk.java.net/pipermail/core-libs-dev/2010-March/003995.html

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store