import { IOptional, Mapper, Optional, Supplier } from '@emprespresso/pengueno'; export interface ICons extends Iterable { readonly value: T; readonly next: IOptional>; readonly replace: Mapper>; readonly before: Mapper>, ICons>; } export class Cons implements ICons { constructor( public readonly value: T, public readonly next: IOptional> = Optional.none(), ) {} public before(head: IOptional>): ICons { return new Cons(this.value, head); } public replace(_value: T): ICons { return new Cons(_value, this.next); } *[Symbol.iterator]() { for (let cur = Optional.some>(this); cur.present(); cur = cur.flatMap((cur) => cur.next)) { yield cur.get().value; } } static addOnto(items: Iterable, tail: IOptional>): IOptional> { return Array.from(items) .reverse() .reduce((cons, value) => Optional.from>(new Cons(value, cons)), tail); } static from(items: Iterable): IOptional> { return Cons.addOnto(items, Optional.none()); } } export interface IZipper extends Iterable { readonly read: Supplier>; readonly next: Supplier>>; readonly previous: Supplier>>; readonly prependChunk: Mapper, IZipper>; readonly prepend: Mapper>; readonly remove: Supplier>; readonly replace: Mapper>; } export class ListZipper implements IZipper { private constructor( private readonly reversedPathToHead: IOptional>, private readonly currentHead: IOptional>, ) {} public read(): IOptional { return this.currentHead.map(({ value }) => value); } public next(): IOptional> { return this.currentHead.map>( (head) => new ListZipper(Optional.some(head.before(this.reversedPathToHead)), head.next), ); } public previous(): IOptional> { return this.reversedPathToHead.map>( (lastVisited) => new ListZipper(lastVisited.next, Optional.some(lastVisited.before(this.currentHead))), ); } public prependChunk(values: Iterable): IZipper { return new ListZipper(Cons.addOnto(Array.from(values).reverse(), this.reversedPathToHead), this.currentHead); } public prepend(value: T): IZipper { return this.prependChunk([value]); } public remove(): IZipper { const newHead = this.currentHead.flatMap((right) => right.next); return new ListZipper(this.reversedPathToHead, newHead); } public replace(value: T): IZipper { const newHead = this.currentHead.map((right) => right.replace(value)); return new ListZipper(this.reversedPathToHead, newHead); } *[Symbol.iterator]() { let head: ListZipper = this; for (let prev = head.previous(); prev.present(); prev = prev.flatMap((p) => p.previous())) { head = >prev.get(); } if (head.currentHead.present()) yield* head.currentHead.get(); } public collection() { return Array.from(this); } static from(iterable: Iterable): ListZipper { return new ListZipper(Optional.none(), Cons.from(iterable)); } }