/* * This file is ported from * * Scala (https://www.scala-lang.org) * * Copyright EPFL and Lightbend, Inc. * * Licensed under Apache License 2.0 * (http://www.apache.org/licenses/LICENSE-2.0). */ export const ListTypeId = Symbol.for("@tsplus/collections/List") export type ListTypeId = typeof ListTypeId export const _A = Symbol.for("@tsplus/collections/List/_A") export type _A = typeof _A /** * @tsplus type List */ export type List = Cons | Nil type ConsNS = Cons type NilNS = Nil export declare namespace List { export type Cons = ConsNS export type Nil = NilNS } /** * @tsplus type List.Ops */ export interface ListOps { readonly $: ListAspects } export const List: ListOps = { $: {} } /** * @tsplus type List.Aspects */ export interface ListAspects {} export declare namespace List { /** * @tsplus type NonEmptyList */ export type NonEmpty = Cons } /** * @tsplus type List/Cons */ export class Cons implements Collection, Equals { readonly _tag = "Cons" readonly [_A]!: (_: never) => A readonly [ListTypeId] = ListTypeId constructor(readonly head: A, public tail: List) {} [Symbol.iterator](): Iterator { let done = false // eslint-disable-next-line @typescript-eslint/no-this-alias let these: List = this return { next() { if (done) { return this.return!() } if (these._tag === "Nil") { done = true return this.return!() } const value: A = these.head these = these.tail return { done, value } }, return(value?: unknown) { if (!done) { done = true } return { done: true, value } } } } [Hash.sym](): number { return Hash.iterator(this[Symbol.iterator]()) } [Equals.sym](that: unknown): boolean { return that instanceof Cons && equalsWith(that, Equals.equals)(this) } } /** * @tsplus type List/Nil */ export class Nil implements Collection, Equals { readonly _tag = "Nil" readonly [_A]!: (_: never) => A readonly [ListTypeId] = ListTypeId; [Symbol.iterator](): Iterator { return { next() { return { done: true, value: undefined } } } } [Hash.sym](): number { return Hash.iterator(this[Symbol.iterator]()) } [Equals.sym](that: unknown): boolean { return that instanceof Nil } } export const _Nil = new Nil() /** * @tsplus unify List * @tsplus unify List/Nil * @tsplus unify List/Cons */ export function unify>( self: X ): List<[X] extends [{ readonly [_A]: (_: never) => infer A }] ? A : never> { return self } /** * @tsplus static List.Ops nil */ export function nil(): Nil { return _Nil } /** * @tsplus static List.Ops cons */ export function cons(head: A, tail: List): Cons { return new Cons(head, tail) } /** * @tsplus fluent List isNil */ export function isNil(self: List): self is Nil { return self._tag === "Nil" } /** * @tsplus fluent List isCons */ export function isCons(self: List): self is Cons { return self._tag === "Cons" } /** * Returns the number of elements contained in a `List` * * @tsplus getter List length */ export function length(self: List): number { let these = self let len = 0 while (!isNil(these)) { len += 1 these = these.tail } return len } /** * @tsplus static List.Aspects equalsWith * @tsplus pipeable List equalsWith */ export function equalsWith( that: List, f: (a: A, b: B) => boolean ) { return (self: List): boolean => { if ((self as List) === that) { return true } else if (length(self) !== length(that)) { return false } else { const i0 = self[Symbol.iterator]() const i1 = that[Symbol.iterator]() let a: IteratorResult let b: IteratorResult while (!(a = i0.next()).done && !(b = i1.next()).done) { if (!f(a.value, b.value)) { return false } } return true } } } /** * @tsplus pipeable-operator List == * @tsplus static List.Aspects equals * @tsplus pipeable List equals */ export function equals(that: List): (self: List) => boolean export function equals(that: List): (self: List) => boolean export function equals(that: List) { return (self: List): boolean => self.equalsWith(that, Equals.equals) } /** * Type guard * * @tsplus static List.Ops isList */ export function isList(u: Iterable): u is List export function isList(u: unknown): u is List export function isList(u: unknown): u is List { return typeof u === "object" && u != null && ListTypeId in u }