The goal of lazer is to enable lazy evaluation for iterables.
Lazer aims to achieve its goal with three guiding principles
- Minimal TypeScript sugar - code should be easily translatable to regular ES6 JavaScript. Ideally, simply erasing type annotations would be the ES6 you would have written without TypeScript.
- No prototype extension - to avoid conflicts or changes to JavaScript in the future, no adding methods to
Array.prototype
,Map.prototype
, or any other native JavaScript data structures. - Minimal to no data structure lock-in - many JavaScript libraries that provide extensions for native types force you to use a special syntax or data structure to enable the extensions. While there is absolutely nothing wrong with that (and is often preferred), it locks you into that library's style. Once you're locked in, it can be hard to migrate to future enhancements to core JavaScript.
Lazer uses generators on stand-alone functions to enable this functionality.
Traditional Array.prototype
methods like map, filter, reduce, etc. are provided as stand-alone functions instead of methods on a stream or sequence data structure. If a function takes multiple arguments, it is explicitely curried to provide maximum flexibility.
A traditional JavaScript Array
method chain might look like this:
const arr =
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.filter(n => n % 2 === 0)
.map(n => n * 10); // [20, 40, 60, 80, 100]
To create the same ending final Array
with lazer, you would write:
const arr = collect(toArray)(map(n => n * 10)(filter(n => n % 2 === 0)([1, 2, 3, 4, 5, 6, 7, 8, 9, 10].values())));
Gross, I know. If you're eyes are bleeding after looking at the exapmle, lazer provides a small Sequence
class and Seq
static class to facilitate a more fluent interface. Using Seq
and Sequence
, the lazer example can be written like this:
const arr =
Seq.of(1, 2, 3, 4, 5, 6, 8, 9, 10) // could also be written `Seq.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10].values())
.andThen(filter(n => n % 2 === 0))
.andThen(map(n => n * 10))
.collect(); // [20, 40, 60, 80, 100]
Use andThen
to chain sequences together followed by a collect
method to process the sequence. collect
defaults to using the toArray
function to transfrom the sequence into an array, but collect
has the option to take a collector function to obtain different result types.
For exmaple, a sum
function could be passed to collect
to return the sum of all numbers in the sequence instead of an array:
const res =
Seq.of(1, 2, 3, 4, 5, 6, 8, 9, 10) // could also be written `Seq.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10].values())
.andThen(filter(n => n % 2 === 0))
.andThen(map(n => n * 10))
.collect(sum); // 300
Lazer's explicitely curried functions make it so that if a pipe operator makes it into JavaScript, the same sequence could be written something like this (depending on which pipe operator makes it into the spec):
const arr =
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|> filter(n => n % 2 === 0)
|> map(n => n * 10)
|> collect(toArray)
Lazer is not going to be the right approach for everyone, and might not seem very Typescript-y or even JavaScript-y.
If you want a different library with similar aims, try out Sequency or Streamjs. Immutable js, Lodash, and RxJS may satisfy your needs as well.
As an alternative, if you don't want to deal with finding suite of JavaScript packages but still want a fully featured solution to many JavaScript pain points, try ClojureScript