These tools however are not a good fit for infinite sequences as they always consume the whole sequence at once to return a new one. Implementing infinite sequences by yourself means you would have to come up with your own API that clients need to adhere to. You often would keep state variables whose values need to be maintained for the duration of the computation process.
Generators to the rescue
Using ES6 generators implementing the infinite sequence of all natural numbers turns out to be a trivial task. We even have language support to iterate over them.
Now that we have a first infinite set we need a couple of functions that help us working with, combining, and building new sequences.
Let us start with map() - a function at the very heart of functional programming. It builds a new sequence by applying a function to all elements of a given sequence.
Using the generator implementation of map() we can now easily write a function called squares() that represents the set of squares of all natural numbers (1², 2², 3², …, n²).
As we are using for…of we can also pass an Array to map() to retrieve a new generator with a finite source. The given function is applied to value after value instead of to all values at once when using Array.prototype.map.
Another common task is filtering specific values from a sequence. Our custom implementation of filter() takes an iterator and a predicate - the returned sequence will consist of all items of the original one for which the predicate holds.
We can now use filter() to create the set of all even natural numbers.
A common derivation from filter() is filterNot() that simply negates the given predicate. We can use that to implement even() as well.
Suppose we were to implement a sequence that represents all Mersenne prime numbers. Mersenne primes are defined as prime numbers of the form Mn = 2n - 1, that is the set of all numbers of the given form that have no positive divisors other than 1 and themselves. The set of Mersenne primes is assumed to be infinite though this remains unproven, yet.
Let us first define some helper functions. range(from, to) and forall() are common helpers in functional programming languages. range() returns the set of natural numbers in a given range. forall() returns whether the given predicate holds for all items in the sequence and should therefore only be used for finite sequences.
mersenneNumbers() is the set of all numbers of the form Mn = 2n - 1. isPrime() is a very simple and naive (and slow) primality checker that returns whether the given candidate is divisible by any of the numbers in the range of [2, candidate - 1]. We will use isPrime() as a filter to remove all non-prime numbers from mersenneNumbers().
As a last example we will implement a function that flattens nested sequences.
Note that using for…of comes in handy again as we can use it to iterate over Arrays and generators. Using flatten() we can now do:
Combining flatten() and map() to flatMap() we can implement another very common function that flattens the result of applying a given function to all items of a sequence. Let us use it to re-build the set of all natural numbers from the set of all even natural numbers.
Generators are powerful