I’m working on my iterator library and I’ve come up with what I think is a convenient API. Here are some examples of how to use it:
// API usages
const iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// First of all we have to cast iterable
// into IterableWrapper to have ability to // chain another iterators
const iter = intoIterable(iterable)
.filter((x) => x > 4)
.map((x) => x + 100)
.take(2);
// intoIterable
function intoIterable<T>(iterable: Iterable<T>): IterableWrapper<T> {
return IterableWrapper.new(iterable);
}
// IterableWrapper
type MapFn<T, U> = (item: T) => U;
type FilterFn<T> = (item: T) => boolean;
class IterableWrapper<T> {
iterable: Iterable<T>;
iterator: Iterator<T>;
constructor(iterable: Iterable<T>) {
this.iterable = iterable;
this.iterator = iterable[Symbol.iterator]();
}
static new<T>(iterable: Iterable<T>): IterableWrapper<T> {
return new IterableWrapper(iterable);
}
[Symbol.iterator]() {
return this;
}
next() {
return this.iterator.next();
}
// Modifiers
map<U>(fn: MapFn<T, U>): IterableWrapper<U> {
const iter = this.iterator;
const mapIterable = createMapIterable(iter, fn);
const iterableWrapper = IterableWrapper.new(mapIterable);
return iterableWrapper;
}
filter(fn: FilterFn<T>): IterableWrapper<T> {
const iter = this.iterator;
const filterIterable = createFilterIterable(iter, fn);
const iterableWrapper = IterableWrapper.new(filterIterable);
return iterableWrapper;
}
// Aggregators
sum(this: IterableWrapper<number>): number {
const sumResult = sum(this.iterable);
return sumResult;
}
// Other
take(n: number): IterableWrapper<T> {
const iter = this.iterator;
const takeIterable = createTakeIterable(iter, n);
const iterableWrapper = IterableWrapper.new(takeIterable);
return iterableWrapper;
}
}
export default IterableWrapper;
// createMapIterable
type MapFn<T, U> = (item: T) => U;
function map<T, U>(iter: Iterator<T>, fn: MapFn<T, U>): Iterable<U> {
return {
[Symbol.iterator]() {
return {
next: () => {
const { done, value } = iter.next();
return done ? { done, value } : { done, value: fn(value) };
},
};
},
};
}
export default map;
createFilterIterable is almost the same as createMapIterable.
I have a few questions:
- Performance: Is this approach effective from a performance perspective? Each time an iterator method is called, a brand new IterableWrapper is returned, which could become quite large after adding all possible iterators. Does this matter given modern JIT compilation optimizations? If my approach is not efficient, could you recommend how it could be improved without changing the API?
- API Design: What do you think about the API? The contracts are pretty simple, so I don’t anticipate major issues, but I’d appreciate any insights or potential pitfalls.