Last active
October 22, 2023 10:35
-
-
Save nodew/4a50dc46cde3e1113d0c1e2bc60d5668 to your computer and use it in GitHub Desktop.
monad with typescript
This file contains hidden or 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
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