Skip to content

Instantly share code, notes, and snippets.

@erodactyl
Last active July 11, 2022 06:06
Show Gist options
  • Save erodactyl/568aee5af425f8dd741f999375f0c788 to your computer and use it in GitHub Desktop.
Save erodactyl/568aee5af425f8dd741f999375f0c788 to your computer and use it in GitHub Desktop.
Maybe monad implemented functionally in typescript
class None {}
class Some<T> {
constructor(public value: T) {}
}
type Maybe<T> = None | Some<T>;
export const none = new None();
export const some = <T>(val: T) => new Some(val);
export const maybe = <T>(val: T | null) => {
return val === null ? none : some(val);
};
export const nonEmpty = <T>(m: Maybe<T>): m is Some<T> => {
if (m === none) return false;
return true;
};
/**
* Unwraps a Maybe object with value. Can only be called on Some<T>.
* Confirm that the object is Some<T> before calling.
*/
export const unwrap = <T>(m: Some<T>): T => {
return m.value;
};
export const bind = <T, V>(m: Maybe<T>, operator: (val: T) => V) => {
if (!nonEmpty(m)) return none;
return some(operator(unwrap(m)));
};
export const match = <T, V>(
m: Maybe<T>,
matcher: {
some: (val: T) => V;
none: () => V;
}
) => {
if (!nonEmpty(m)) return matcher.none();
return matcher.some(unwrap(m));
};
// USAGE
const getUser = () => (Math.random() < 0.5 ? null : { username: "Erik" });
const user = maybe(getUser());
const username = bind(user, (u) => u.username);
const initials = bind(username, (un) => un.slice(0, 2));
const len = bind(initials, (in_) => in_.length);
const final = match(len, {
some: (l) => l * 2,
none: () => 8,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment