Created
January 8, 2019 05:04
-
-
Save christianscott/285ab3a3bd5c2da341aee0124c85ecb1 to your computer and use it in GitHub Desktop.
Typescript Maybe Monad (WIP)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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