Skip to content

Instantly share code, notes, and snippets.

@joshburgess
Created January 6, 2019 11:05
Show Gist options
  • Save joshburgess/d9d448315991aa42d83fef21afd16163 to your computer and use it in GitHub Desktop.
Save joshburgess/d9d448315991aa42d83fef21afd16163 to your computer and use it in GitHub Desktop.
Simulating a recursive linked List data type (a la Haskell, PureScript, etc.) in TypeScript
// A list is either empty (represented by the `Nil` constructor) or non-empty, in
// which case it consists of a head element, and another list (represented by the
// `Cons` constructor).
//
// data List a = Nil | Cons a (List a)
interface Nil {
readonly tag: 'Nil'
}
interface Cons<A, ListOfA extends List<A>> {
readonly tag: 'Cons'
readonly head: A
readonly tail: ListOfA
}
interface List_<A> {
readonly tag: 'List'
readonly values: Cons<A, List<A>> // this is the most important part
}
type List<A> = Nil | List_<A>
// we are simulating tagged unions or "algebraic data types" from other languages
// see the Discriminated Unions section here:
// https://www.typescriptlang.org/docs/handbook/advanced-types.html
@joshburgess
Copy link
Author

joshburgess commented Jan 6, 2019

@karol-majewski
Copy link

karol-majewski commented Jan 6, 2019

@joshburgess The only thing that comes to mind is that we can avoid this name collision by introducing a separate constructor (in exactly the same way global JavaScript objects like Object or Array are defined).

interface Nil {
  readonly tag: 'Nil';
}

interface Cons<T, U extends List<T>> {
  readonly tag: 'Cons';
  readonly head: T,
  readonly tail: U;
}

interface List<T> {
  readonly tag: 'List';
  readonly values: Cons<T, List<T>>;
}

interface ListConstructor {
  new <T>(): Nil | List<T>;
}

declare const List: ListConstructor;

Then we can use it like this:

declare function isNil(argument: unknown): argument is Nil;

const numbers = new List<number>();

isNil(numbers)
  ? 42
  : numbers.values.head;

Although it's hardly an improvement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment