Collection Operations on Lazy Sequences in TypeScript with itiriri

I’m currently working in [TypeScript](https://www.typescriptlang.org/docs/handbook/basic-types.html) after spending a lot of time in [F#](https://fsharpforfunandprofit.com/why-use-fsharp/), and I’ve been experimenting with adapting my favored functional approaches to the former.

Recently, an [Exercism.io](https://spin.atomicobject.com/2015/05/24/programming-exercises/) puzzle called for iterating over a grid. A year ago, I would have done this procedurally:

for i=0 to 10
    for j=0 to 10
        // do stuff here

More recently, I’ve favored breaking a problem into a series of steps with clearly stated (and type-checked!) intermediate states:


getPoints(w,h).forEach(p => /* do the work*/)

In F#, I like to implement methods like `getPoints()` with [lazy sequences](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/sequences):


let getPoints w h =
    seq {
        for i in [0..w] do
            for j in [0..h] do
                yield (i,j)
    }

Java(Type)Script has this too, in the form of [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*). This seemed like a good opportunity to try them:


function* getPoints(w: number, h: number) {
  for (var i = 0; i < w; i++) {
    for (var j = 0; j < h; j++) {
      console.log(`generating [${i},${j}]..`);
      yield [i, j];
    }
  }
}

...and we have a sequence of points! Great! Now let's map over them, and...

Imagine my disappointment. For some reason, ES6 [iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) don't support common collection methods like map, reduce, filter, etc.

## How to Proceed
If you don't actually need the [laziness](https://en.wikipedia.org/wiki/Lazy_evaluation), you can quickly turn the sequence into an array with [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax):


[...getPoints()].map(x => /* (...) */

This would be fine for my programming puzzle, but I'm a restless idealist, and I want something better.

I briefly looked into whether it'd be possible to hack `map()` onto iterables via [prototype extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#Bad_practice_Extension_of_native_prototypes), but then I remembered how much I dislike working with prototypes and quickly turned back.

I checked with Lodash, and while the functional JavaScript library has some lazy evaluation [capabilities](https://lodash.com/docs/4.17.11#lodash) of its own, it doesn't [generally support](https://github.com/lodash/lodash/issues/737) ES6 iterators.

Finally, deep in a comment thread somewhere, I found the answer.

## itiriri
[itiriri](https://github.com/labs42io/itiriri) is a functional utility library like Lodash, but _built for ES6 iterators_. And it's written in TypeScript, so its type definitions are unlikely to lie to you!

Finally, I can functionally operate on my sequence:


import { query } from "itiriri";

query(getPoints(10, 10))
  .filter(p => (p[0] + p[1]) % 3 == 0)
  .map(p => `(${p[0]},${p[1]})`)
  .take(5)
  .forEach(s => console.log(s));

Though it's not needed here, it's really cool that this runs lazily. You can see that the _generation_ work is interspersed amongst the _consumption_ work:

generating [0,7]..
generating [0,8]..
generating [0,9]..
(0,9)
generating [1,0]..
generating [1,1]..
generating [1,2]..
(1,2)

I should note that this isn't just for generator functions: JavaScript's data structures [are iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#Built-in_iterables), too, so itiriri will happily operate on them!

## Conclusion
JavaScript's map and filter have [been around](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Specifications) since ECMAScript 5.1 in 2011. Generators and iterators [appeared](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*#Specifications) in 2015 with the [for..of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) loop to traverse them. Hopefully, the cross product of these—functional collection operations on lazy sequences—will come out of the box in some future language version.

Until then, I'm glad to have found itiriri.

### Further Reading:
- [Experiments in Purely Functional TypeScript
](https://spin.atomicobject.com/2018/02/05/purely-functional-typescript/)
- [Understanding and Embracing TypeScript’s “readonly”
](https://spin.atomicobject.com/2017/08/14/typescript-readonly-intro/)

Conversation
  • Vlad Negura says:

    Itiriri co-author here.
    Hey thank you for posting this, I really appreciate.
    If you encounter any issues or have any suggestion on how to improve itiriri, let me know.

    Cheers,
    Vlad.

  • Comments are closed.