src/list.ts (182 lines of code) (raw):

/** * Copyright (c) 2016-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import * as Maybe from './maybe'; enum ListType { nil, cons, } export class List<T> { private listType: ListType; private value?: T; private next?: List<T>; constructor(listType: ListType, value?: T, next?: List<T>) { this.listType = listType; this.value = value; this.next = next; } match<U>(nil: () => U, cons: (value: T, next: List<T>) => U) { switch (this.listType) { case ListType.nil: return nil(); case ListType.cons: return cons(this.value!, this.next!); } } } export function of<T>(...args: T[]): List<T> { if (args.length === 0) { return new List<T>(ListType.nil); } else { var list = new List<T>(ListType.nil); for (var i: number = args.length - 1; i >= 0; i--) { list = cons(args[i], list); } return list; } } function returnTrue() { return true; } function returnFalse() { return false; } export function isEmpty<T>(list: List<T>): boolean { return list.match(returnTrue, returnFalse); } export function length<T>(list: List<T>): number { return list.match( function () { return 0; }, function (val: T, next: List<T>) { return 1 + length(next); }, ); } export function cons<T>(elem: T, list: List<T>): List<T> { return new List<T>(ListType.cons, elem, list); } function returnJustHead<T>(val: T, tail: List<T>): T | null { return val; } export function head<T>(list: List<T>): T | null { return list.match(function () { return null; }, returnJustHead); } function returnTail<T>(head: T, tail: List<T>): List<T> { return tail; } export function tail<T>(list: List<T>): List<T> { return list.match(function () { return of<T>(); }, returnTail); } function prependToAll<T>(seperator: T, list: List<T>): List<T> { return list.match( function () { return list; }, function (head: T, tail: List<T>) { return cons(seperator, cons(head, prependToAll(seperator, tail))); }, ); } export function intersperse<T>(seperator: T, list: List<T>): List<T> { return list.match( function () { return list; }, function (head: T, tail: List<T>) { return cons(head, prependToAll(seperator, tail)); }, ); } export function append<T>(list1: List<T>, list2: List<T>): List<T> { if (isEmpty(list2)) { return list1; } else if (isEmpty(list1)) { return list2; } else { return foldr( function (soFar: List<T>, thisVal: T) { return cons(thisVal, soFar); }, list2, list1, ); } } export function filter<T>(f: (t: T) => boolean, list: List<T>): List<T> { return foldr( function (soFar: List<T>, val: T) { if (f(val)) { return cons(val, soFar); } else { return soFar; } }, of<T>(), list, ); } export function map<T, U>(f: (t: T) => U, list: List<T>): List<U> { return list.match( function () { return of<U>(); }, function (val: T, next: List<T>) { return new List<U>(ListType.cons, f(val), map(f, next)); }, ); } export function foldl<T, U>( f: (soFar: U, thisOne: T) => U, initialValue: U, list: List<T>, ): U { return list.match( function () { return initialValue; }, function (val: T, next: List<T>) { return foldl(f, f(initialValue, val), next); }, ); } export function foldr<T, U>( f: (soFar: U, thisOne: T) => U, initialValue: U, list: List<T>, ): U { return list.match( function () { return initialValue; }, function (val: T, next: List<T>) { return f(foldr(f, initialValue, next), val); }, ); } export function reverse<T>(listToReverse: List<T>): List<T> { return foldl( function (soFar: List<T>, val: T): List<T> { return cons(val, soFar); }, of<T>(), listToReverse, ); } export function fromArray<T>(array: T[]): List<T> { return array.reduceRight(function (existing: List<T>, thisOne: T) { return cons(thisOne, existing); }, of<T>()); } export function toArray<T>(list: List<T>): T[] { return foldl( function (soFar: T[], thisOne: T) { soFar.push(thisOne); return soFar; }, [], list, ); }