UNPKG

stdlazy

Version:

Flexible and debuggable lazy primitive.

149 lines (96 loc) 5.41 kB
# stdlazy TypeScript-first lazy evaluation for people used to promises. ## Install Using yarn: ```typescript yarn add stdlazy ``` Or npm: ```typescript npm install --save stdlazy ``` ## Usage ```typescript const syncLazy = lazy(() => { console.log("Running sync operation") return syncOperation() } const asyncLazy = lazy(async () => {} ) ``` ## Pullables A _Pullable_ is an object that provides a `pull()` that returns a value, which is also called its _pull value_. The `pull` method is **idempotent** – calling it more than once should return the same result and cause no additional side-effects. Like a _Thenable_, a _Pullable_ will never produce another _Pullable_. If this would happen, it will instead **pull** it by invoking the `pull()` method, and return whatever it returns. This means _Pullables_ can be chained transparently, just like promises. The type operator that gives the _pull value_ of a type `T` (which might be a _Pullable_) is `Pulled<T>`. It works just like `Awaited<T>` The `Lazy` object offered by `stdlazy` is a _Pullable_, but has a lot more functionality besides. ## Construction Use the `lazy` factory function to wrap another function. This function can be sync or async, and can even return another _Pullable_ that will be flattened. ```typescript import { lazy } from "stdlazy" const lz = lazy(() => 1) satisfies Lazy<number> const lzLz = (lazy(() => lazy1).pull() + 1) satisfies number const lzAsync = lazy(async () => 1) satisfies LazyAsync<number> ``` `stdlazy` uses the same type – `Lazy<T>` – to represent both sync and async lazy values. An async lazy value is just a `Lazy<Promise<T>>`, which is also referred under the alias `LazyAsync<T>`. This is also known as its **FINAL FORM**. The factory function will flatten all standard compositions of the `Lazy` and `Promise` types into this **FINAL FORM**. ```typescript lazy(async () => { return lazy(async () => { return 5 }) }) satisfies LazyAsync<number> ``` ## Operators The `Lazy` type implements several operators for working with lazy values. ### Map _Project the (awaited) pull value of a Lazy instance._ ```typescript // The operator projects the Pulled value of a Lazy: const lz = lazy(() => 1).map(x => x + 1) console.log(lz.pull()) // 2 // If the lazy is async, it projects that Pulled and Awaited value instead, cutting through both container types. const asyncLz = lazy(async () => 1).map(x + 1) console.log(await lz.pull()) // 2 ``` The `map` operator creates a new `Lazy` value, backed by an existing one. When it’s **pulled**, it will **pull** the existing instance and project its (awaited) pull value using a function. This callback won’t be executed until the `pull()` method on the outermost `Lazy` instance is called and, if it returns a Promise, that Promise resolves. If the projection returns another _Pullable_, its `pull()` method will be called automatically. This allows chaining _Pullables_ just like chaining promises. If either the current `Lazy` instance or the projection is async, the result will be a `LazyAsync`. - [`Array#map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) - [`Promise#then`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) ### Do _Perform an action with the (awaited) pull value of a Lazy instance._ ```typescript const lz = lazy(() => 1).do(console.log) console.log(lz.pull()) // 1 ``` The `do` operator produces a new `Lazy` instance, backed by an existing one. When it’s **pulled**, it will **pull** the existing instance and invoke the given callback before producing the value unchanged. - [`rxjs/do`](https://www.learnrxjs.io/learn-rxjs/operators/utility/do) If either the current `Lazy` instance or the callback is async, the result will be `LazyAsync`. ### Zip _Combines a `Lazy` with one or more other Pullables into a single `Lazy` value._ ```typescript const lz1 = lazy(() => 1) const lz2 = lazy(() => 2) const zipped = lz1.zip(lz2) console.log(zipped.pull()) // [1, 2] ``` The `zip` operator zips together an existing `Lazy` value with other _Pullables_, producing a single `Lazy` producing an array of their values, in the order in which they were specified. - [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) - [`_.zip`](https://lodash.com/docs/4.17.15#zip) - [`rxjs/zip`](https://www.learnrxjs.io/learn-rxjs/operators/combination/zip) - Python [`zip`](https://docs.python.org/3.3/library/functions.html#zip) If any of the `Lazy` instances involved are async, the result will be a `LazyAsync`. ### Assemble _Combine multiple pullables into a single pullable returning an object_ ```typescript const lz1 = lazy(() => 1) const lz2 = lazy(() => "hello") const assembled = lz1.assemble({ other: lz2 }) console.log(assembled.pull()) // {this: 1, other: "hello"} ``` The `assemble` operator combines an existing `Lazy` instance with other _Pullables_, given as an object with _Pullable_ values. It returns a single `Lazy` instance with the same keys and the values replaced by the pull values of each of them _Pullables_ The `Lazy` instance on which the operation was performed is considered to have the key `this` If any of hte `Lazy` instances involved are async, the result will be `LazyAsync`.