Skip to content

Instantly share code, notes, and snippets.

@co1rowjp
Created October 6, 2012 19:39
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save co1rowjp/3845888 to your computer and use it in GitHub Desktop.
Save co1rowjp/3845888 to your computer and use it in GitHub Desktop.
TypeScript Maybe
interface Functor {
fmap: (any) => any;
}
interface Monad extends Functor {
bind: (any) => Monad;
}
interface Maybe extends Monad {
}
class Just implements Maybe {
private value: any;
constructor(a: any) {
this.value = a
}
fmap (f: (any) => any): Just {
return new Just(f(this.value))
}
bind (f: (any) => Maybe): Maybe {
return f(this.value)
}
}
class Nothing implements Maybe {
fmap (f: (any) => any): Nothing {
return this
}
bind (f: (any) => Maybe): Maybe {
return this
}
}
@datokrat
Copy link

datokrat commented Apr 3, 2017

This might seem a bit dirty but it gives us the comfort of quite a lot of type inference:

// playground.ts
import {} from "./maybe";

const one = just("Hello World").value; // OK
const bad = nothing().value; // Compile error

const two = just("Hello World").or(nothing()).value; // OK
const three = nothing().or(just("Hello World")).value; // OK
const baaad = nothing().or(nothing()).value; // Compile error

const four = just("Hello World").map(x => `${x}!`).value; // OK
const five = nothing().map(x => `${x}!`).value; // Compile error

const six = just(just("Hello World")).flatten(); // OK; typeof six == Maybe<string>
const seven = just("Hello World").flatten(); // Compile error

function logMaybe(m: Maybe<string>) {
    // Type guards work
    if (isJust(m)) console.log(m.value); // OK
}
// maybe.ts
export function just<T>(value: T): Just<T> {
  return new _Just<T>(value) as Just<T>;
}

export function nothing(): Nothing<any> {
  return new _Nothing() as Nothing<any>;
}

export interface MBase<T> {
  type: "just" | "nothing";
  map<U>(this: Just<T>, project: (t: T) => U): Just<U>;
  map<U>(this: Nothing<T>, project: (t: T) => U): Nothing<U>;
  map<U>(project: (t: T) => U): Maybe<U>;

  or(this: Just<T>, alternative: Maybe<T>): Just<T>;
  or<U>(this: Nothing<T>, alternative: Just<U>): Just<U>;
  or<U>(this: Nothing<T>, alternative: Nothing<U>): Nothing<U>;
  or(this: Maybe<T>, alternative: Just<T>): Just<T>;
  or<U>(this: Nothing<T>, alternative: Maybe<U>): Maybe<U>;
  or(alternative: Maybe<T>): Maybe<T>;

  flatten<U>(this: Maybe<Maybe<U>>): Maybe<U>;
};
export interface Just<T> extends MBase<T> {
  type: "just";
  readonly value: T;
};
export interface Nothing<T> extends MBase<T> {
  type: "nothing";
}

export type Maybe<T> = Just<T> | Nothing<T>;

export function isJust<T>(x: Maybe<T>): x is Just<T> {
  return x.type === "just";
}

export function isNothing<T>(x: Maybe<T>): x is Nothing<T> {
  return x.type === "nothing";
}

abstract class _Maybe<T> implements MBase<T> {
  public type: "nothing" | "just";

  public map<U>(this: Just<T>, project: (t: T) => U): Just<U>;
  public map<U>(this: Nothing<T>, project: (t: T) => U): Nothing<U>;
  public map<U>(this: Maybe<T>, project: (t: T) => U): Maybe<U> {
    const m: Maybe<T> = this;
    return isJust(m) ? just(project(m.value)) : nothing();
  }

  public or(this: Just<T>, alternative: Maybe<T>): Just<T>;
  public or(this: Nothing<T>, alternative: Just<T>): Just<T>;
  public or(this: Nothing<T>, alternative: Nothing<T>): Nothing<T>;
  public or(this: Nothing<T>, alternative: Maybe<T>): Maybe<T>;
  public or(this: Maybe<T>, alternative: Just<T>): Just<T>;
  public or(this: Maybe<T>, alternative: Maybe<T>): Maybe<T> {
    return isJust(this) ? this : alternative;
  }

  public flatten<U>(this: Maybe<Maybe<U>>): Maybe<U> {
    const m: Maybe<Maybe<U>> = this;
    return isJust(m) ? m.value : nothing();
  }
}

class _Nothing extends _Maybe<never> {
  public readonly type = "nothing";
}

class _Just<T> extends _Maybe<T> {
  public readonly type = "just";
  constructor(public readonly value: T) { super() }
}

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