Last active
October 18, 2017 11:05
-
-
Save gneuvill/f3133d50e2759e74b183b1e62e8b97db to your computer and use it in GitHub Desktop.
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
import {HKT as _} from "fp-ts/lib/HKT" | |
import {Equal, equal} from "./eq" | |
import {Functor, lift} from "fp-ts/lib/Functor" | |
import {Monad} from "fp-ts/lib/Monad" | |
import {Foldable} from "fp-ts/lib/Foldable" | |
import {constant} from "fp-ts/lib/function" | |
import {Traversable} from "fp-ts/lib/Traversable" | |
import {Applicative} from "fp-ts/lib/Applicative" | |
declare module "fp-ts/lib/HKT" { | |
interface URI2HKT<A> { | |
Maybe: Maybe<A> | |
} | |
} | |
const _Maybe = "Maybe" | |
type _Maybe = typeof _Maybe | |
abstract class MaybeImpl<A> { | |
readonly kind: "Just" | "Nothing" | |
readonly _A: A | |
readonly _URI: _Maybe | |
isNothing(this: Maybe<A>): boolean { return isNothing(this) } | |
isJust(this: Maybe<A>): boolean { return !this.isNothing() } | |
justOr(this: Maybe<A>, a: () => A): A { return justOr(this, a) } | |
orElse(this: Maybe<A>, o: () => Maybe<A>): Maybe<A> { return orElse(this, o) } | |
bind<B>(this: Maybe<A>, f: (_: A) => Maybe<B>): Maybe<B> { return bind(f, this) } | |
map<B>(this: Maybe<A>, f: (_: A) => B): Maybe<B> { return map(f, this) } | |
forEach(this: Maybe<A>, f: (_: A) => Unit): Unit { return forEach(this, f) } | |
} | |
type Maybe<A> = Just<A> | Nothing<A> | |
class Just<A> extends MaybeImpl<A> { | |
readonly kind: "Just" = "Just" | |
constructor(readonly a: A) { super() } | |
} | |
class Nothing<A> extends MaybeImpl<A> { | |
readonly kind: "Nothing" = "Nothing" | |
} | |
const _nothing = new Nothing() | |
const nothing = <A>(): Maybe<A> => | |
_nothing as Maybe<A> | |
const just = <A>(a: A): Maybe<A> => | |
new Just(a) | |
const from = <A>(a: A | null | undefined): Maybe<A> => | |
//noinspection EqualityComparisonWithCoercionJS | |
a == null ? nothing<A>() : just(a) | |
const fromStr = (str: string | null | undefined): Maybe<string> => | |
from(str).bind(s => s.length === 0 ? nothing<string>() : just(s)) | |
const fold = <A, B>(m: Maybe<A>, f: (_: A) => B, b: () => B): B => { | |
switch (m.kind) { | |
case "Just": return f(m.a) | |
case "Nothing": return b() | |
} | |
} | |
const isJust = <A>(m: Maybe<A>): boolean => | |
!isNothing(m) | |
const isNothing = <A>(m: Maybe<A>): boolean => | |
fold(m, _ => false, () => true) | |
const justOr = <A>(m: Maybe<A>, a: () => A): A => | |
fold(m, _ => _, a) | |
const orElse = <A>(ma: Maybe<A>, mb: () => Maybe<A>): Maybe<A> => | |
isJust(ma) ? ma : mb() | |
const bind = <A, B>(f: (_: A) => Maybe<B>, m: Maybe<A>): Maybe<B> => | |
fold(m, f, () => nothing<B>()) | |
const map = <A, B>(f: (_: A) => B, m: Maybe<A>): Maybe<B> => | |
bind(a => just(f(a)), m) | |
const apply = <A, B>(mab: Maybe<(_: A) => B>, ma: Maybe<A>): Maybe<B> => | |
bind(f => map(f, ma), mab) | |
const exists = <A>(ma: Maybe<A>, p: (_: A) => boolean): boolean => | |
fold(ma, p, () => false) | |
const forall = <A>(ma: Maybe<A>, p: (_: A) => boolean): boolean => | |
fold(ma, p, () => true) | |
const filter = <A>(ma: Maybe<A>, p: (_: A) => boolean): Maybe<A> => | |
bind(a => p(a) ? ma : nothing<A>(), ma) | |
const forEach = <A>(ma: Maybe<A>, f: (_: A) => Unit): Unit => | |
fold(ma, f, () => Unit.unit) | |
const foreach = <A>(ma: Maybe<A>, f: (_: A) => void): void => | |
fold(ma, f, () => {}) | |
const eq = <A>(eqa: Equal<A>): Equal<Maybe<A>> => | |
equal(ma1 => ma2 => { | |
switch (ma1.kind) { | |
case "Just": return exists(ma2, eqa._eq(ma1.a)) | |
case "Nothing": return ma2.isNothing() | |
} | |
}) | |
const mapFlipped = <f, A, B>(F: Functor<f>, fa: _<f, A>, f: (a: A) => B): _<f, B> => | |
F.map(f, fa) | |
class Instances implements Monad<_Maybe>, Traversable<_Maybe> { | |
readonly URI: _Maybe | |
readonly of = just | |
readonly ap = apply | |
readonly map = map | |
readonly chain = bind | |
readonly reduce = <A, B>(f: (b: B, a: A) => B, b: B, fa: Maybe<A>): B => | |
fold(fa, a => f(b, a), constant(b)) | |
readonly traverse = <F>(A: Applicative<F>) => <A, B>(f: (a: A) => _<F, B>, ta: Maybe<A>): _<F, Maybe<B>> => | |
fold(ta, x => mapFlipped(A, f(x), just), () => A.of(nothing())) | |
} | |
const maybe = new Instances() | |
export { | |
Maybe | |
, maybe | |
, nothing | |
, just | |
, from | |
, fromStr | |
, fold | |
, isJust | |
, isNothing | |
, justOr | |
, orElse | |
, bind | |
, map | |
, exists | |
, forall | |
, filter | |
, forEach | |
, foreach | |
, eq | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment