Skip to content

Instantly share code, notes, and snippets.

@nodew
Last active October 22, 2023 10:35
Show Gist options
  • Save nodew/4a50dc46cde3e1113d0c1e2bc60d5668 to your computer and use it in GitHub Desktop.
Save nodew/4a50dc46cde3e1113d0c1e2bc60d5668 to your computer and use it in GitHub Desktop.
monad with typescript
interface Functor<A> {
fmap<B>(fn: (a: A) => B): Functor<B>
}
abstract class Applicative<A> implements Functor<A> {
static of: <A>(a: A) => Applicative<A>;
abstract fmap<B>(fn: (a: A) => B): Applicative<B>;
abstract ap<B, C>(this: Applicative<(b: B) => C>, ma: Applicative<B>): Applicative<C>;
lift<B, C>(fn: (a: A, b: B) => C) : (mb: Applicative<B>) => Applicative<C> {
return (mb: Applicative<B>) => {
return this.fmap(a => (b: B) => fn(a, b)).ap<B, C>(mb);
}
};
}
abstract class Monad<A> extends Applicative<A> {
static of: <A>(a: A) => Monad<A>;
abstract chain<B>(transform: (a: A) => Monad<B>): Monad<B>
}
class Identity<A> extends Monad<A> {
private _context: A;
constructor(context: A) {
super();
this._context = context;
}
get context(): A {
return this._context;
}
fmap<B>(fn: (a: A) => B): Identity<B> {
return new Identity<B>(fn(this.context))
}
static of<A>(a: A) : Identity<A> {
return new Identity<A>(a);
}
ap<B, C>(this: Identity<(b: B) => C>, mb: Identity<B>): Identity<C> {
return this.fmap(f => f(mb.context));
}
chain<B>(transform: (a: A) => Identity<B>): Identity<B> {
return transform(this.context);
}
}
class Just<T> {
_value: T;
constructor(value: T) {
this._value = value;
}
get value() {
return this._value;
}
}
class Nothing {}
class Maybe<A> extends Monad<A> {
private _context: Just<A> | Nothing;
constructor(context: Just<A> | Nothing) {
super();
this._context = context;
}
get context() {
return this._context;
}
static just<A>(value: A) {
return new Maybe<A>(new Just<A>(value));
}
static nothing<A>() {
return new Maybe<A>(new Nothing())
}
isJust(): boolean {
return this.context instanceof Just;
}
isNothing(): boolean {
return this.context instanceof Nothing;
}
fmap<B>(fn: (a: A) => B): Maybe<B> {
if (this.context instanceof Nothing) {
return new Maybe<B>(this.context);
}
return new Maybe<B>(fn(this.context.value))
}
static of<A>(a) : Maybe<A> {
return Maybe.just<A>(a);
}
ap<B, C>(this: Maybe<(value: B) => C>, mb: Maybe<B>): Maybe<C> {
if (this.context instanceof Nothing) {
return new Maybe<C>(this.context);
}
const context = mb.context;
if (context instanceof Just) {
return this.fmap(f => f(context.value));
}
return new Maybe<C>(context);
}
chain<B>(transform: (value: A) => Maybe<B>): Maybe<B> {
if (this.context instanceof Nothing) {
return new Maybe<B>(this.context)
}
return transform(this.context.value);
}
}
function doM(gen) {
function step(value?) {
const result = gen.next(value);
if (result.done) {
return result.value;
}
return result.value.chain(step);
}
return step();
}
interface Array<T> {
chain: <B>(transform: (value: T) => B[]) => B[];
}
Array.prototype.chain = function<B>(transform: ((value) => B[])) : B[] {
return this.map(transform).reduce((acc, curr) => {
acc = acc.concat(curr);
return acc;
}, []);
}
const val =
[1, 2, 3, 4]
.chain(a => [a * 2]
.chain(b => [b + 2]
.chain(c => [a + b + c])))
console.log(val);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment