There has been some discussions lately about the need to support generators now that Virtual threads are a thing. I am reading up some examples and I still don't understand what is a generator and why is it powerful.<p>Some examples from Python: https://www.programiz.com/python-programming/generator<p>Some examples using plain Java 8: https://www.mguenther.net/2016/01/functional_generators_in_java_8/index.html<p>I am a little confused why this is regarded as a powerful pattern. It seems I can implement the same using a normal `Iterator`. For instance, from the Python example:<p><pre><code> class PowTwoGen {
final int max;
int n = 0;
PowTwoGen(int max) { this.max = max;}
boolean hasNext() { return n <= max;}
int next() { return Math.pow(2, n++); }
}
</code></pre>
The examples for the Java 8 article can also be done with a simple method call but instead the examples with the Generator seems overly complex. What am I missing?
I think that guy just made up a generator class for fun. It’s not too different from the interator except it doesn’t have a hasNext() method so it either returns results forever or it has to return a sentinel value like <i>null</i> or return an exception to end iteration.<p>Somebody could make the case that returning a sentinel value or an exception is a better API since there is no risk somebody else is going to call the next() method after you call hasNext() and next(). Writing a generator that wraps a generator is a little simpler than writing an interest or that wraps an iteration because you don’t have to write a hasNext() function, which can occasionally be awkward.<p>That generator library has a few functions, like <i>map</i> that work on generators, unfortunately the Java stdlib doesn’t come with anything like that. (There is the streams API but it is over-complicated.)<p>I’ll point out this library I wrote<p><a href="https://github.com/paulhoule/pidove">https://github.com/paulhoule/pidove</a><p>which does a lot of what the Steams library does but it works on iterators without creating streams. If you like those generator examples you might like pidove.<p>As for Python it is kinda accidental that generators would up related to coroutines, that is, generators were an easy way to implement coroutines, later async/await and stuff like that got added.
A generator function can always be implemented via an iterator class, but often the generator is more convenient. Without generators, you manually need to convert loops and conditionals (typical programming) into an explicit state machine (atypical programming, and more verbose)<p>Your example in Python:<p><pre><code> class Pow2:
def __init__(self, n):
self.value = 0
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.value < self.n:
ans = self.value * self.value
self.value += 1
return ans
else:
raise StopIteration
iter(Pow2(n))
</code></pre>
is semantically equivalent to<p><pre><code> def pow2(n):
value = 0
while value < n:
yield value * value
value += 1
pow2(n)
</code></pre>
and<p><pre><code> (i * i for i in range(n))
</code></pre>
but both the generators are a lot less code.<p>The generator expression (last one) can actually be implemented right now in Java streams (something like `IntRange::to(n).map(i => i * i)`). But the generator functions which use `yield` are necessary for more complex expressions: the ones which have nested loops, resources, exceptions, etc. which are even more verbose when converted into generator classes
> I am a little confused why this is regarded as a powerful pattern<p>The power of the generator is that, unlike an iterator, the collection of values doesn't "exist", it is created on the fly. This comes in to play when iterating over, say the set of integers, or something else that would take significant amounts of memory.<p>Also, as functions, generators can be passed around in any language that treats functions a first-class types.