Skip to content

Instantly share code, notes, and snippets.

@datokrat
Created April 4, 2017 09:30
Show Gist options
  • Save datokrat/d0545e9240d0f3010be8fe298265bbb4 to your computer and use it in GitHub Desktop.
Save datokrat/d0545e9240d0f3010be8fe298265bbb4 to your computer and use it in GitHub Desktop.
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() }
}
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
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment