Skip to content

Instantly share code, notes, and snippets.

@samwgoldman
Last active October 24, 2015 23:34
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 samwgoldman/732c97ef0210e971a3e4 to your computer and use it in GitHub Desktop.
Save samwgoldman/732c97ef0210e971a3e4 to your computer and use it in GitHub Desktop.
/* @flow */
type Maybe2<T,U> = (pattern: {
some: (x: T) => U,
none: () => U
}) => U;
type Maybe<T> = Maybe2<T, *>;
// Constructors
function some<T>(x: T): Maybe<T> {
return pattern => pattern.some(x);
}
const none: Maybe<any> = pattern => pattern.none();
// Combinators
function map<A,B,C>(m: Maybe2<A,C>, f: (x: A) => B): Maybe2<B,C> {
return pattern => m({
some: x => pattern.some(f(x)),
none: pattern.none
});
}
function flatMap<A,B,C>(m: Maybe2<A,C>, f: (x: A) => Maybe2<B,C>): Maybe2<B,C> {
return pattern => m({
some: x => f(x)(pattern),
none: pattern.none
});
}
// Unwrap
function get<T>(m: Maybe<T>): ?T {
return m({
some: x => x,
none: () => null
});
}
function getOrElse<T>(m: Maybe<T>, orElse: T): T {
return m({
some: x => x,
none: () => orElse
});
}
// Util
function log(m: Maybe<any>): void {
m({
some: x => console.log("Some:", x),
none: () => console.log("None")
})
}
// Parsing
function string(x: mixed): Maybe<string> {
return pattern => {
if (typeof x === "string") {
pattern.some(x);
} else {
pattern.none();
}
}
}
function number(x: mixed): Maybe<number> {
return pattern => {
if (typeof x === "number") {
pattern.some(x);
} else {
pattern.none();
}
}
}
function object(x: mixed): Maybe<Object> {
return pattern => {
if (typeof x === "object" && x != null) {
pattern.some(x);
} else {
pattern.none();
}
}
}
function prop(o: Object, p: string, f: (x: any) => Maybe<any>): Maybe<Object> {
return map(f(o[p]), () => o);
}
function props(
o: Object,
ps: { [p: string]: (x: any) => Maybe<any> }
): Maybe<Object> {
return Object.keys(ps).reduce(
(m, p) => flatMap(m, o => prop(o, p, ps[p])),
some(o)
);
}
// Example
type Foobar = { foo: string, bar: number };
function foobar(x: mixed): Maybe<Foobar> {
return flatMap(object(x), o => props(o, {
foo: string,
bar: number
}));
}
var unsafe: Foobar = JSON.parse("{}"); // any -> Foobar is OK
var safe_none: Maybe<Foobar> = foobar(JSON.parse("{}"));
var safe_some: Maybe<Foobar> = foobar(JSON.parse('{ "foo": "", "bar": 0 }'));
try {
unsafe.foo.length; // Error
} catch (e) {
console.error("Error:", e.message);
}
log(safe_none); // None
log(safe_some); // Some: { foo: "", bar: 0 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment