$30 off During Our Annual Pro Sale. View Details »

Discussion: switching iterator helpers to functions

Discussion: switching iterator helpers to functions

Axel Rauschmayer

September 07, 2022
Tweet

More Decks by Axel Rauschmayer

Other Decks in Programming

Transcript

  1. Discussion: switching iterator
    helpers to functions

    View Slide

  2. Status quo: iteration API
    interface Iterable {


    [Symbol.iterator]() : Iterator;


    }


    interface Iterator {


    next() : IteratorResult;


    }


    interface IteratorResult {


    value: T;


    done: boolean;


    }

    View Slide

  3. 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.

    View Slide

  4. 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

    View Slide

  5. 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)

    View Slide

  6. More than one operand
    Iterator methods:
    arr1.values().zip(arr2.values())


    arr1.values().zip(arr2)
    Functions on iterables:
    zip(arr1, arr2)

    View Slide

  7. 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(_);


    View Slide

  8. 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

    View Slide

  9. 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).

    View Slide

  10. 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.

    View Slide

  11. 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.

    View Slide

  12. 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?

    View Slide

  13. 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

    View Slide