summaryrefslogtreecommitdiff
path: root/lib/types/collections/cons.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/types/collections/cons.ts')
-rw-r--r--lib/types/collections/cons.ts108
1 files changed, 108 insertions, 0 deletions
diff --git a/lib/types/collections/cons.ts b/lib/types/collections/cons.ts
new file mode 100644
index 0000000..b671d71
--- /dev/null
+++ b/lib/types/collections/cons.ts
@@ -0,0 +1,108 @@
+import { IOptional, Mapper, Optional, Supplier } from '@emprespresso/pengueno';
+
+export interface ICons<T> extends Iterable<T> {
+ readonly value: T;
+ readonly next: IOptional<ICons<T>>;
+
+ readonly replace: Mapper<T, ICons<T>>;
+ readonly before: Mapper<IOptional<ICons<T>>, ICons<T>>;
+}
+
+export class Cons<T> implements ICons<T> {
+ constructor(
+ public readonly value: T,
+ public readonly next: IOptional<ICons<T>> = Optional.none(),
+ ) {}
+
+ public before(head: IOptional<ICons<T>>): ICons<T> {
+ return new Cons<T>(this.value, head);
+ }
+
+ public replace(_value: T): ICons<T> {
+ return new Cons<T>(_value, this.next);
+ }
+
+ *[Symbol.iterator]() {
+ for (let cur = Optional.some<ICons<T>>(this); cur.present(); cur = cur.flatMap((cur) => cur.next)) {
+ yield cur.get().value;
+ }
+ }
+
+ static addOnto<T>(items: Iterable<T>, tail: IOptional<ICons<T>>): IOptional<ICons<T>> {
+ return Array.from(items)
+ .reverse()
+ .reduce((cons, value) => Optional.from<ICons<T>>(new Cons<T>(value, cons)), tail);
+ }
+
+ static from<T>(items: Iterable<T>): IOptional<ICons<T>> {
+ return Cons.addOnto(items, Optional.none());
+ }
+}
+
+export interface IZipper<T> extends Iterable<T> {
+ readonly read: Supplier<IOptional<T>>;
+ readonly next: Supplier<IOptional<IZipper<T>>>;
+ readonly previous: Supplier<IOptional<IZipper<T>>>;
+
+ readonly prependChunk: Mapper<Iterable<T>, IZipper<T>>;
+ readonly prepend: Mapper<T, IZipper<T>>;
+ readonly remove: Supplier<IZipper<T>>;
+ readonly replace: Mapper<T, IZipper<T>>;
+}
+
+export class ListZipper<T> implements IZipper<T> {
+ private constructor(
+ private readonly reversedPathToHead: IOptional<ICons<T>>,
+ private readonly currentHead: IOptional<ICons<T>>,
+ ) {}
+
+ public read(): IOptional<T> {
+ return this.currentHead.map(({ value }) => value);
+ }
+
+ public next(): IOptional<IZipper<T>> {
+ return this.currentHead.map<IZipper<T>>(
+ (head) => new ListZipper<T>(Optional.some(head.before(this.reversedPathToHead)), head.next),
+ );
+ }
+
+ public previous(): IOptional<IZipper<T>> {
+ return this.reversedPathToHead.map<IZipper<T>>(
+ (lastVisited) => new ListZipper<T>(lastVisited.next, Optional.some(lastVisited.before(this.currentHead))),
+ );
+ }
+
+ public prependChunk(values: Iterable<T>): IZipper<T> {
+ return new ListZipper<T>(Cons.addOnto(Array.from(values).reverse(), this.reversedPathToHead), this.currentHead);
+ }
+
+ public prepend(value: T): IZipper<T> {
+ return this.prependChunk([value]);
+ }
+
+ public remove(): IZipper<T> {
+ const newHead = this.currentHead.flatMap((right) => right.next);
+ return new ListZipper<T>(this.reversedPathToHead, newHead);
+ }
+
+ public replace(value: T): IZipper<T> {
+ const newHead = this.currentHead.map((right) => right.replace(value));
+ return new ListZipper<T>(this.reversedPathToHead, newHead);
+ }
+
+ *[Symbol.iterator]() {
+ let head: ListZipper<T> = this;
+ for (let prev = head.previous(); prev.present(); prev = prev.flatMap((p) => p.previous())) {
+ head = <ListZipper<T>>prev.get();
+ }
+ if (head.currentHead.present()) yield* head.currentHead.get();
+ }
+
+ public collection() {
+ return Array.from(this);
+ }
+
+ static from<T>(iterable: Iterable<T>): ListZipper<T> {
+ return new ListZipper(Optional.none(), Cons.from(iterable));
+ }
+}