import { nat } from "./primitives.js"; import { assertTrue, freeze, privateConstructor } from "./utils.js"; class ListIterator implements Iterator { #list : List constructor(list : List) { this.#list = list; freeze(this); } next(): IteratorResult { if (this.#list.isEmpty) return { done : true, value : undefined }; else { const value = this.#list.head!; this.#list = this.#list.tail!; return { done : false, value : value } } } } freeze(ListIterator); /** An immutable list. */ export class List implements Iterable { static #internal = false; #tail : List | undefined; #head : V | undefined; #length : number; private constructor(head : V | undefined, tail : List | undefined) { if (!List.#internal) privateConstructor("List"); this.#tail = tail; this.#head = head; this.#length = tail === undefined ? 0 : tail.#length + 1; freeze(this); } static empty : List = (() => { List.#internal = true; const list = new List(undefined, undefined); List.#internal = false; return list; })(); static is(value : any) : value is List { if (value instanceof List) return true; else return false; } static from(...values : V[]) : List { let l : List = List.empty; for (let i = values.length-1; i >= 0; i--) { l = l.cons(values[i]); } return l; } get isEmpty() : boolean { return this.#tail === undefined; } get head() : V | undefined { return this.#head; } get tail() : List | undefined { return this.#tail; } get(index : nat) : V { assertTrue(nat.is(index)); let l : List = this; let i = index; while (i > 0) { if (l.#tail === undefined) throw Error("List.get(" + index + ")"); l = l.#tail; i--; } if (l.#head === undefined) throw Error("List.get(" + index + ")"); return l.#head; } get length() : number { return this.#length; } cons(head : V) : List { List.#internal = true; const list = new List(head, this); List.#internal = false; return list; } [Symbol.iterator]():Iterator { return new ListIterator(this); } toString() : string { const es = [...this].map(v => "" + v); return es.join("→"); } } freeze(List);