This is only marginally better, as the computation logic is still "hidden" in the lambda.
This is a very weird statement. It's hidden in exactly the place where it's needed! Where is it hidden from? Where else would you need it? If you're reading the code, it's right there, you don't have to click to definition like you do with Pair::next.
What should I get from reading Pair::next? There's no natural "next" for pairs of numbers. How would I guess that this guy's idea of "next" involves incrementing the first number and then squaring it? To make this equally readable you'd have to rename "Pair" or "next" so that the names were just as informative and easy to read as the lambda itself. So you end up with an unwieldy-but-meaningful name like SquareNumbersIterationState or SquaresSequence.State, which is the classic hallmark of OO programming run amok (aka, the Kingdom of Nouns.)
It's so much nicer to have the lambda inline and see exactly what's happening. One of my favorite things about functional style is that it doesn't force you to noun and name things that are easy to describe but hard to name. Sometimes something can just stand for itself instead of needing an awkward name. I mean, Java isn't the best at this, but this isn't hard to read at all:
pair -> new Pair(pair.index + 1, Math.pow(pair.index + 1, 2)))
I really like your summary of the situation. I'd add that the author seems to have tried to fix a functional readability problem with OO techniques, ending up with the same kingdom of nouns problem - the worst of both worlds.
I think the functional solution would be to separate increment and transform: Stream.iterate(0, i -> i + 1).map(i -> new Pair(i, Math.Pow(i, 2));
> Notice how state was introduced? It made the code easier to read.
Correct me if I'm wrong, but the only state in that snippet lives in Stream.iterate(), the Fibonacci object is still immutable and next() on it is a pure function. To me this still looks very much like a functional programming approach.
Which just shows that moving business logic into properly named abstractions is good, no matter the programming paradigm.
The real stateful example is the IncrementSupplier and the impure get() method. I personally don't really like it ( Stream.iterate(0, i -> i + 1) is pretty readable to me), but that's probably just personal taste.
Right, his Fibonacci class is completely immutable, i.e. it's purely functional.
He's confusing different usages of the word "state" - exhortations to avoid state are almost always about mutable state, but he doesn't appear to recognize that distinction.
If we take the OP post as definitional, then eliminating state means eliminating compound data structures, because that's the usage of "state" implied by that Fibonacci example.
Hopefully the person at your work has something less extreme in mind. Ask him to explain what he means by "state".
Your code is doing something pretty different from the examples above. You're just calculating the nth fibonacci number, not creating an infinite fibonacci _sequence_. Eliding the explicit recursive structure is the point of the exercise!
Admittedly Python's facilities for doing that with some mutable state are pretty nice:
from itertools import islice
def fib():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
list(islice(fib(), 10)) == [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
As an aside, as each term of this particular sequence only depends on the index, not the previous term, the cleanest way would be to just map an index sequence:
// Full overflow prevention left as an exercise to the reader
IntStream.rangeClosed(0, Integer.MAX_VALUE).map(n -> Math.pow(n, 2))
Streams are a bit more general. They allow you to use functional operations on sequence of elements (e.g. map, filter, reduce, etc.) to build a processing pipeline which can execute async and in parallel.
It's more comparable to Python iterators in combination with itertools, sequence comprehensions, and a few other things.
It's arguably more powerful in that it's got some more bits and bobs out-of-the-box, and it's easier to do parallel stuff with it. But it's also arguably less powerful in that it's not particularly open for anyone who isn't a JDK developer to adapt for their own purposes. To make your own Python iterator that will interact nicely with anything else that's designed to operate on iterators, you only have one method you have to implement. To make your own implementation of Java's Stream interface, you've got a rather daunting list of 40 you have to implement.
Essentially yes - a Java stream returns an iterable object, which we can perform further operations such as filtering down, mapping to something else or 'collecting' it into another object (et al).
I've been writing Java for 20 years. Streams are a definite improvement, but half the time I find myself rewriting a streams approach to an old-fashioned loop to make the code more readable.
When you say "readable," do you mean to OO programmers? Functional programmers? All programmers?
For example, as a 5-year Clojure convert who wrote Java for 15 years before that, old-fashioned loops are less readable now, as they hide the essentials of what is really happening. For example, you might be mapping a collection from one type of value to another, or reducing it to some other form, etc. But both use loops. You have to scan all over the place in the loop construct to see what it's really doing. I'd much rather see the function that is happening than the procedure by which the function is accomplished.
I don't think the stateful approach with .generate() is thread-safe if you make the stream parallel. The API doc says it's an unordered stream, parallelizing it would mean concurrent reads/writes in the supplier.
Stream.iterate() docs on the other hand guarantee happens-before ordering for invocations.
This is a very weird statement. It's hidden in exactly the place where it's needed! Where is it hidden from? Where else would you need it? If you're reading the code, it's right there, you don't have to click to definition like you do with Pair::next.
What should I get from reading Pair::next? There's no natural "next" for pairs of numbers. How would I guess that this guy's idea of "next" involves incrementing the first number and then squaring it? To make this equally readable you'd have to rename "Pair" or "next" so that the names were just as informative and easy to read as the lambda itself. So you end up with an unwieldy-but-meaningful name like SquareNumbersIterationState or SquaresSequence.State, which is the classic hallmark of OO programming run amok (aka, the Kingdom of Nouns.)
It's so much nicer to have the lambda inline and see exactly what's happening. One of my favorite things about functional style is that it doesn't force you to noun and name things that are easy to describe but hard to name. Sometimes something can just stand for itself instead of needing an awkward name. I mean, Java isn't the best at this, but this isn't hard to read at all: