Skip to content

Instantly share code, notes, and snippets.

@christianscott
Created January 8, 2019 05:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christianscott/285ab3a3bd5c2da341aee0124c85ecb1 to your computer and use it in GitHub Desktop.
Save christianscott/285ab3a3bd5c2da341aee0124c85ecb1 to your computer and use it in GitHub Desktop.
Typescript Maybe Monad (WIP)
export module Maybe {
enum Variant {
Just = 1,
Nothing,
}
export type Nothing = Readonly<{ type: Variant.Nothing }>;
export type Just<T> = Readonly<{ type: Variant.Just; value: T }>;
export type Maybe<S> = Nothing | Just<S>;
const _nothing: Nothing = { type: Variant.Nothing };
export function nothing() {
return _nothing;
}
export function just<T>(value: T): Just<T> {
return { type: Variant.Just, value };
}
export function from<T>(value: T | undefined): Maybe<T> {
return value == null ? nothing() : just(value);
}
export function isJust<T>(maybe: Maybe<T>): maybe is Just<T> {
return maybe.type === Variant.Just;
}
export function isNothing<T>(maybe: Maybe<T>): maybe is Nothing {
return maybe.type === Variant.Nothing;
}
export function map<T, R>(maybe: Maybe<T>, fn: (t: T) => R): Maybe<R> {
return isJust(maybe) ? from(fn(maybe.value)) : nothing();
}
export function compose<A, B>(maybeA: Maybe<A>, action: (a: A) => Maybe<B>): Maybe<B> {
return isJust(maybeA) ? action(maybeA.value) : nothing();
}
}
module Deserialize {
export function requiredString(key: string, data: object) {
if (key in data) {
return data[key];
}
throw new Error(`key ${key} missing in object`);
}
export function requiredNumber(key: string, data: object): number {
if (key in data) {
const s = data[key];
return parseInt(s, 10);
}
throw new Error(`key ${key} missing in object`);
}
}
class User {
constructor(readonly name: string, readonly id: number) {}
static deserialize(o: object): Maybe.Maybe<User> {
try {
return Maybe.from(new User(Deserialize.requiredString('name', o), Deserialize.requiredNumber('id', o)));
} catch (error) {
return Maybe.nothing();
}
}
}
async function fetchNextUser(user: User): Promise<Maybe.Maybe<User>> {
const response = await fetch(`/api/users/${user.id + 1}`);
const body = await response.json();
return User.deserialize(body);
}
const user = User.deserialize({ name: 'Christian', id: 0 });
Maybe.map(user, user => user.id);
Maybe.compose(user, fetchNextUser); // error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment