Skip to content

Instantly share code, notes, and snippets.

@gneuvill
Last active October 18, 2017 11:05
Show Gist options
  • Save gneuvill/f3133d50e2759e74b183b1e62e8b97db to your computer and use it in GitHub Desktop.
Save gneuvill/f3133d50e2759e74b183b1e62e8b97db to your computer and use it in GitHub Desktop.
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