protocol Functor { fmap; }
const nothing = Symbol("nothing");
class Maybe implements Functor {
/*private*/ constructor(value) { this._value = value; }
static just(value) { return new Maybe(value) }
static nothing() { return new Maybe(nothing) }
fmap<B>(fn) {
return this._value === nothing
? this
: Maybe.just(fn(this._value));
}
}
Pseudo-TypeScript version:
protocol Functor<A> {
fmap<B>(fn: (a: A) => B): Functor<B>;
// ^ not quite, shouldn't be widened, should be, e.g. Maybe<B>
}
const nothing = Symbol("nothing");
type Just<A> = A;
type Nothing = typeof nothing;
class Maybe<A> implements Functor<Just<A> | Nothing> {
private constructor(private value: Just<A> | Nothing) { }
static just<A>(value: A) { return new Maybe(value) }
static nothing<A>() { return new Maybe<A>(nothing) }
fmap<B>(fn: (a: A) => B) {
return this.value === nothing
? this
: Maybe.just(fn(this.value as A));
}
}
Lets try Applicative
.
-- Haskell
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
// JS
protocol Applicative extends Functor { static pure; ap; }
const nothing = Symbol("nothing");
class Maybe implements Applicative {
static pure(value) { return Maybe.just(value) }
ap(mb) {
if (this._value === nothing) return this;
return mb[Functor.fmap](this._value);
}
// just, nothing, fmap elided
}
Bring forth the Monad
!
-- Haskell
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
fail _ = Nothing
protocol Monad extends Applicative {
bind;
join(mb) { return this[Monad.bind](() => mb); }
static fail(msg) { throw new Error(msg); }
}
class Maybe implements Monad {
bind(fn) {
if (this._value === nothing) return this;
return fn(this._value);
}
static fail() { return Maybe.nothing() }
}