# Migration guide for Lumino 1 to Lumino 2 ## Iterables, iterators, and generators ### Overview and suggestions on iteration These are some guiding principles that came up over the course of refactoring the way we handle iteration in Lumino. They are not hard and fast rules, rather they are intuitions that arose while implementing the changes Lumino 2. ### Iterate natively instead of using `each(...)` Iterating through an iterable `bar: Iterable` using native `for...of`, e.g., `for (const foo of bar) {...}` is a better option than using `each(bar, foo => ...)`. All invocations of `each(...)` have been removed in Lumino 2. See, for example, [this commit](https://github.com/jupyterlab/lumino/pull/346/commits/efb1e919bb359192caeedb726e16ec42d17b3b0f). ### Use `[].forEach(...)` sparingly Now that we support native ES2018 iteration, `for (const value of someArray) {...}` should be favored over `someArray.forEach(...)` because it will not require a context shift every time it invokes the function being applied. ### Use `[Symbol.iterator]()` sparingly Unless you need a handle on multiple iterators simultaneously (e.g., the way `zip(...)` is implemented in Lumino 2) or you need to hold on to multiple values of your iterable during iteration (e.g., the way we need both the first and the second value of an iterable to implement `reduce(...)` in Lumino 2), most of the time you can simply use `for...of` to iterate through any object that has a `Symbol.iterator` method without invoking that method. In many places where the Lumino `iter()` utility function has been replaced in Lumino 2 it is not replaced with an invocation of the new `Symbol.iterator` method. ### Consider `yield*` usage carefully If you have a method or function that returns an iterator, you might simply return values using the `yield` keyword. If you are returning the contents of another iterable, you can use `yield*`. However, if your logic depends on some predicate, remember that the predicate is checked _when you iterate_ if you use `yield*`. For example, consider the following: ```typescript const source = [1, 2, 3, 4, 5]; let flagged = false; function* counter(): IterableIterator { if (!flagged) { yield* source; } } // This is how a client would use the `counter()` function. const iterable = counter(); flagged = true; console.log(Array.from(iterable)); // [] ``` Instead, if we modify the code: ```typescript import { empty } from '@lumino/algorithm'; const source = [1, 2, 3, 4, 5]; let flagged = false; function counter(): IterableIterator { if (!flagged) { return source[Symbol.iterator](); } return empty(); } // This is how a client would use the `counter()` function. const iterable = counter(); flagged = true; console.log(Array.from(iterable)); // [1, 2, 3, 4. 5] ``` In these two examples, the client code that consumes `counter()` is identical. But the results are different. Both implementations are _correct_, but depending on your use case, one may be more appropriate than the other. The important thing to consider is that the two implementations are _not_ identical and yield different results. ### Use `Array.from(...)` sparingly `toArray(...)` has been deprecated. You may be tempted to swap in `Array.from(...)` when you update your code. This _will_ work, but if you simply need to iterate through an iterable, you can use `for...of` directly on the iterable object. This is more performant both in terms of CPU and memory than allocating and populating new `Array` instance before iteration. If you need a snapshot of every item in your iterable as an array, then `Array.from(...)` is an appropriate replacement for `toArray(...)`. ### Look out for `iterator.next() !== undefined` This pattern may appear in your code if you were using Lumino 1 iterators. Instead of checking `iterator.next().done`, as is the case with native iterators, Lumino 1 iterators set `next()` to the value `undefined` as an optimization to prevent allocating an object with `.value` and `.done`, so when migrating from Lumino 1 to Lumino 2, this is a common cause of confusion. For example, a `while` loop in Lumino 1 may have had this predicate: ```typescript while ((it = it.next()) !== undefined) { // loop logic } ``` The native (Lumino 2) version of this loop would be: ```typescript while (!(it = it.next()).done) { // loop logic } ``` ## Public API changes ### `@lumino/algorithm` All of the iterator utilities have been changed to use native generators and iterators. | | `export` | name | note | | --- | ----------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ❌ | `type` | `IterableOrArrayLike` | Switch to [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol) | | ❌ | `interface` | `IIterable` | Switch to `Iterable` | | ❌ | `interface` | `IIterator` | Switch to [`Iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterator_protocol) / `IterableIterator` | | ❌ | `function` | `iter(...)` | Switch to [`Symbol.iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator), [`function*`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*), [`yield*`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield*), etc. | | ❌ | `function` | `iterFn(...)` | Switch to `function*` | | ❌ | `function` | `iterItems(...)` | We aren't using this function anywhere | | ❌ | `function` | `iterKeys(...)` | Switch to [`for...in`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) | | ❌ | `function` | `iterValues(...)` | Switch to [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) | | ❌ | `class` | `ArrayIterator` | Switch to `[][Symbol.iterator]()` or `yield*` | | ❌ | `class` | `ChainIterator` | Previous implementation of `chain()` | | ❌ | `class` | `EmptyIterator` | Previous implementation of `empty()` | | ❌ | `class` | `EnumerateIterator` | Previous implementation of `enumerate()` | | ❌ | `class` | `FilterIterator` | Previous implementation of `filter()` | | ❌ | `class` | `FnIterator` | Switch to [generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) | | ❌ | `class` | `ItemIterator` | We aren't using this class anywhere | | ❌ | `class` | `KeyIterator` | Switch to `for...in` | | ❌ | `class` | `MapIterator` | Previous implementation of `map()` | | ❌ | `class` | `RangeIterator` | Previous implementation of `range()` | | ❌ | `class` | `RetroIterator` | Previous implementation of `retro()` | | ❌ | `class` | `StrideIterator` | Previous implementation of `stride()` | | ❌ | `class` | `TakeIterator` | Previous implementation of `take()` | | ❌ | `class` | `ValueIterator` | Switch to `for...of` | | ❌ | `class` | `ZipIterator` | Previous implementation of `zip()` | | ☑️ | `function` | `chain(...)` | `@deprecated`, use native [`yield`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield) instead | | ☑️ | `function` | `each(...)` | `@deprecated`, use native `for...of`, `[].forEach`, etc. | | ✅ | `function` | `empty(...)` | Reimplement with native types | | ✅ | `function` | `enumerate(...)` | Reimplement with native types | | ✅ | `function` | `every(...)` | Reimplement with native types | | ✅ | `function` | `filter(...)` | Reimplement with native types | | ✅ | `function` | `find(...)` | Reimplement with native types | | ✅ | `function` | `findIndex(...)` | Reimplement with native types | | ✅ | `function` | `map(...)` | Reimplement with native types | | ✅ | `function` | `max(...)` | Reimplement with native types | | ✅ | `function` | `min(...)` | Reimplement with native types | | ✅ | `function` | `minmax(...)` | Support native types | | ☑️ | `function` | `once(...)` | `@deprecated`, use native `yield` instead | | ✅ | `function` | `reduce(...)` | Support native types | | ✅ | `function` | `range(...)` | Reimplement with native types | | ☑️ | `function` | `repeat(...)` | `deprecated`, use native `while` loop with `yield` | | ✅ | `function` | `retro(...)` | Reimplement with native types | | ✅ | `function` | `some(...)` | Reimplement with native types | | ✅ | `function` | `stride(...)` | Reimplement with native types | | ✅ | `function` | `take(...)` | Reimplement with native types | | ☑️ | `function` | `toArray(...)` | `@deprecated`, use [`Array.from(...)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) or [`for...of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) | | ✅ | `function` | `toObject(...)` | Reimplement with native types | | ✅ | `function` | `topologicSort(...)` | Support native types | | ✅ | `function` | `zip(...)` | Reimplement with native types | ### `@lumino/collections` `LinkedList` has been updated to accept native iterables and return native iterators. | | `export` | name | note | | --- | ---------- | --------------------------------- | ---------------------------------------------------- | | ❌ | `class` | `LinkedList.ForwardValueIterator` | Switch to `LinkedList#[Symbol.iterator]` | | ❌ | `class` | `LinkedList.RetroValueIterator` | Previous implementation of `LinkedList#retro()` | | ❌ | `class` | `LinkedList.ForwardNodeIterator` | Previous implementation of `LinkedList#nodes()` | | ❌ | `class` | `LinkedList.RetroNodeIterator` | Previous implementation of `LinkedList#retroNodes()` | | ❌ | `method` | `LinkedList#iter()` | Switch to `LinkedList#[Symbol.iterator]` | | ✅ | `function` | `LinkedList.from(...)` | Accept `Iterable` | | ✅ | `method` | `LinkedList#assign(...)` | Accept `Iterable` | | ✅ | `method` | `LinkedList#nodes()` | Return `IterableIterator>` | | ✅ | `method` | `LinkedList#retro()` | Return `IterableIterator` | | ✅ | `method` | `LinkedList#retroNodes()` | Return `IterableIterator>` | ### `@lumino/datagrid` `DataGrid` selections are now native iterators. | | `export` | name | note | | --- | -------- | ---------------------------------- | --------------------------------------------------- | | ✅ | `method` | `BasicSelectionModel#selections()` | Return `IterableIterator` | | ✅ | `method` | `SelectionModel#selections()` | Return `IterableIterator` | ### `@lumino/disposable` Helper functions for `DisposableSet` and `ObservableDisposableSet` have been udpated. | | `export` | name | note | | --- | ---------- | ----------------------------------- | ------------------------------ | | ✅ | `function` | `DisposableSet.from(...)` | Accept `Iterable` | | ✅ | `function` | `ObservableDisposableSet.from(...)` | Accept `Iterable` | ### `@lumino/dragdrop` | | `export` | name | note | | --- | ----------- | ------------------ | --------------------------------------- | | ❌ | `type` | `DropAction` | Moved to `Drag.DropAction` | | ❌ | `type` | `SupportedActions` | Moved to `Drag.SupportedActions` | | ☑️ | `interface` | `IDragEvent` | `@deprecated`, use `Drag.Event` instead | ### `@lumino/widgets` `Layout` and its sub-classes now use native iterators, e.g. `implements Iterable`. | | `export` | name | note | | --- | -------- | ------------------------------ | --------------------------------------------- | | ❌ | `method` | `DockLayout#iter()` | Switch to `DockLayout#[Symbol.iterator]` | | ❌ | `method` | `GridLayout#iter()` | Switch to `GridLayout#[Symbol.iterator]` | | ❌ | `method` | `Layout#iter()` | Switch to `Layout#[Symbol.iterator]` | | ❌ | `method` | `PanelLayout#iter()` | Switch to `PanelLayout#[Symbol.iterator]` | | ❌ | `method` | `SingletonLayout#iter()` | Switch to `SingletonLayout#[Symbol.iterator]` | | ✅ | `method` | `DockLayout#handles()` | Return `IterableIterator` | | ✅ | `method` | `DockLayout#selectedWidgets()` | Return `IterableIterator` | | ✅ | `method` | `DockLayout#tabBars()` | Return `IterableIterator>` | | ✅ | `method` | `DockLayout#widgets()` | Return `IterableIterator` | | ✅ | `method` | `DockPanel#handles()` | Return `IterableIterator` | | ✅ | `method` | `DockPanel#selectedWidgets()` | Return `IterableIterator` | | ✅ | `method` | `DockPanel#tabBars()` | Return `IterableIterator>` | | ✅ | `method` | `DockPanel#widgets()` | Return `IterableIterator` | | ✅ | `method` | `Widget#children()` | Return `IterableIterator` |