Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Discussion: switching iterator helpers to funct...

Discussion: switching iterator helpers to functions

Axel Rauschmayer

September 07, 2022
Tweet

More Decks by Axel Rauschmayer

Other Decks in Programming

Transcript

  1. Status quo: iteration API interface Iterable<T> { [Symbol.iterator]() : Iterator<T>;

    } interface Iterator<T> { next() : IteratorResult<T>; } interface IteratorResult<T> { value: T; done: boolean; }
  2. Status quo: iteration-based mechanisms All built-in iteration-based mechanisms operate on

    iterables (not iterators): • Array.from() • for-of • Array-destructuring • Promise.all() • yield* Programmers normally don’t see iterators.
  3. Status quo: libraries Popular libraries that operate on data structures:

    • Lodash/Underscore: functions & wrappers • Immutable.js: OOP with deep inheritance hierarchy (Collection.Keyed etc.) • Uses iterables often. • Ramda: functions
  4. Handling various values Iterator methods: • New API is supported:

    get iterator • arr → arr.values().drop(3) • map → map.entries().drop(3) • str → str[Symbol.iterator]().drop(3) • arr.keys() → arr.keys().drop(3) • New API is not supported: • itrbl → Iterator.from(itrbl).drop(3) Functions on iterables: • arr → drop(3, arr) • map → drop(3, map) • str → drop(3, str) • arr.keys() → 
 drop(3, arr.keys()) • itrbl → drop(3, itrbl)
  5. How important is chaining? // Chaining const arr = new

    Set([0, -1, 3]) .values() .filter(x => x >= 0) .map(x => x * 2) .toArray(); // Naming the steps const set = new Set([0, -1, 3]); const filtered = filter(x => x>=0, set); const mapped = map(x => x*2, filtered); const arr = toArray(mapped); // Single variable pattern let _ = new Set([0, -1, 3]); _ = filter(x => x >= 0, _); _ = map(x => x * 2, _); const arr = toArray(_);
  6. How important is chaining? // Chaining const arr = new

    Set([0, -1, 3]) .values() .filter(x => x >= 0) .map(x => x * 2) .toArray(); // Pipe operator const arr = new Set([0, -1, 3]) |> filter(x => x >= 0, %) |> map(x => x * 2, %) |> toArray(%); • All of the upsides of iterator methods, • none of the downsides
  7. Pros and cons of iterator methods Cons: • Signi fi

    cant change of current protocol • Programmers have to know: • Does a value support the API? • How to get an iterator. • Iterator.from(value) as common pattern? • Why not a wrapping API then? • Libraries can’t add methods to API. • Consequence: mix of API styles 
 Pros: • Chainable • Implicit namespace (no “import” needed) • Value-speci fi c implementations of operations are used automatically (override iterator method).
  8. Pros and cons of functions Cons: • Value-speci fi c

    implementations of operations are not used automatically: • Solution 1: Use different function. • Solution 2: Delegate to symbol-keyed methods (similar: string → RegExp). • No chaining • Namespaced (must qualify or “import” via destructuring) 
 Pros: • No need to change the existing protocol. • Same style as current built-in iteration-based mechanisms • Follow precedents such as Lodash and Ramda. • Easy to apply to operands • Same style as complementary libraries. • Keep options open w.r.t. pipe operator.
  9. Observations • Two iteration APIs would be too much. •

    Thus – choose between: • Iterator methods • Functions on iterables • jQuery-style wrapping • I’d be happy to help with a function-based API.
  10. Open questions • Overlapping functionality: • Array.fromAsync() vs. AsyncIterable.toArray(asyncIterable) •

    Related: Array.from() vs. Iterable.toArray(syncIterable) • Function signature style? • Main operand fi rst (OOP)? • Better for normal function calls and hack pipes • Main operand last (partial application)? • Better for .bind() • A function-based API would work well as a built-in module. In the cards?
  11. Material • Implementation of a function-based version of iterator helpers:

    
 https://github.com/rauschma/iterable • Contacting me: • https://twitter.com/rauschma • https://2ality.com/p/about.html#contact