1 of 12

Joint Iteration

for Stage 1

Michael Ficarra • September 2023

2 of 12

motivation

  • have 2+ iterators that are positionally aligned
  • want to process corresponding values together

3 of 12

current state-of-the-art: write your own helper 🙁

function* zip<A, B>(as: Iterable<A>, bs: Iterable<B>): Iterable<[A, B]> {

const iterA = as[Symbol.iterator]();

const iterB = bs[Symbol.iterator]();

while (true) {

const iterResultA = iterA.next();

const iterResultB = iterB.next();

if (iterResultA.done !== iterResultB.done) {

throw new Error('iterators ended at different times');

}

if (iterResultA.done) {

break;

}

yield [iterResultA.value, iterResultB.value];

}

}

4 of 12

likely shape: static method(s) on Iterator

Iterator.zip(as, bs);

or

Iterator.zip(as, bs, cs, ...);

5 of 12

design space: does zipWith obviate zip?

Iterator.zipWith(Math.max, as, bs, cs, ...);

// Math.max(a0, b0, c0), Math.max(a1, b1, c1), ...

Iterator.zipWith(Array.of, as, bs, cs, ...);

// [a0, b0, c0], [a1, b1, c1], ...

6 of 12

design space: varargs? just 2? Iterable<Iterator>?

Iterator.zip(as, bs);

or

Iterator.zip(as, bs, cs, ...);

or

Iterator.zip([as, bs, cs, ...]);

  • varargs eliminates the possibility of an options bag for other configurability

7 of 12

design space: positional or named iterators?

Iterator.zip({ a: as, b: bs, c: cs });

// { a: a0, b: b0, c: c0 },

// { a: a1, b: b1, c: c1 },

// ...

8 of 12

design space: longest / filled / strict

Iterator.zipLongest(as, bs); // fill with undefined

Iterator.zipFilled(as, bs, filler); // fill with filler

Iterator.zipFilled(as, bs, asFiller, bsFiller);

Iterator.zipStrict(as, bs); // throw

9 of 12

prior art: other languages

language

shortest

longest

privileged

strict

-With

3+ sources

1 source

0 sources

Clojure

variadic map

yes

yes

yes

Elm

List.map2

yes

yes?

Haskell

zip

zipWith

yes

OCaml

zip

map2

yes?

Python

zip

itertools.zip_longest

zip(..., strict=True)

yes

yes

yes, empty

Ruby

Enumerable#zip

zip

yes

yes

Rust

Iterator::zip

Scala

zip

it.zipAll(jt, x, y)

Swift

zip

10 of 12

prior art: JS libraries

library

shortest

longest

privileged

strict

-With

3+ sources

1 source

0 sources

@iterable-iterator/zip

zip

zipLongest

yes

yes

@softwareventures/iterator

zipOnce

extra-iterable

zip

zip

zip

yes

yes

yes, empty

immutable.js

Seq::zip

zipWith

yes

yes

iter-ops

zip

yes

yes

yes, empty

iter-tools

zip

zipAll

yes

yes

yes, empty

iterablefu

zip

zipAll

yes

yes

yes, empty

iterare

zip

itertools-ts

zip

zipFilled/zipLongest

zipEqual

yes

yes

yes, empty

ixjs

zip

yes

yes

yes, empty

lodash

zip

zipWith

yes

yes

yes, empty

ramda

zip

zipWith

wu

zip

zipLongest

zipWith

yes

yes

zipiterators

zipiterators

11 of 12

Champion's Thoughts

12 of 12

Stage 1?