Last active
December 24, 2017 11:38
-
-
Save gerardpaapu/9283992 to your computer and use it in GitHub Desktop.
A lazy Either monad for 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
export interface Monad<T> { | |
chain<TT>(f: (v: T) => Monad<TT>): Monad<TT>; | |
concat(m: Monad<T>): Monad<T>; | |
empty<T>(): Monad<T>; | |
map<A, B>(f: (A: any) => B): Monad<B>; | |
of<A>(v: A): Monad<A>; | |
} | |
export declare class Either<T, E> implements Monad<T> { | |
private doFork; | |
constructor(doFork: (f: (value: T) => Object, g: (error: E) => Object) => Object); | |
static fail<A, F>(error: F): Either<A, F>; | |
static of<A, F>(value: A): Either<A, F>; | |
static empty<A, F>(): Either<A, F>; | |
public fork<A>(f: (value: T) => A, g: (error: E) => A): A; | |
public of<A, F>(value: A): Either<A, F>; | |
public fail<A, F>(error: F): Either<A, F>; | |
public empty(): Either<T, E>; | |
public getValue(): T; | |
public chain<TT>(m: (value: T) => Either<TT, E>): Either<TT, E>; | |
public map<A>(m: (value: T) => A): Either<A, E>; | |
public concat(either: Either<T, E>): Either<T, E>; | |
} | |
export declare class Promise<V, E> implements Monad<V> { | |
private doTap; | |
constructor(doTap: (cb: (e: Either<V, E>) => void) => void); | |
static of<VV, EE>(v: VV): Promise<VV, EE>; | |
static fail<VV, EE>(e: EE): Promise<VV, EE>; | |
static empty<VV, EE>(): Promise<VV, EE>; | |
public of<VV, EE>(v: VV): Promise<VV, EE>; | |
public empty<VV, EE>(): Promise<VV, EE>; | |
public fail<VV, EE>(e: EE): Promise<VV, EE>; | |
public tap(cb: (e: Either<V, E>) => void): void; | |
public map<VV>(f: (_: V) => VV): Promise<VV, E>; | |
public chain<VV>(f: (_: V) => Promise<VV, E>): Promise<VV, E>; | |
public concat(other: Promise<V, E>): Promise<V, E>; | |
} |
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
define(["require", "exports"], function(require, exports) { | |
var Either = (function () { | |
function Either(doFork) { | |
this.doFork = doFork; | |
} | |
Either.fail = function (error) { | |
return new Either(function (f, g) { | |
return g(error); | |
}); | |
}; | |
Either.of = function (value) { | |
return new Either(function (f, g) { | |
return f(value); | |
}); | |
}; | |
Either.empty = function () { | |
return this.fail(null); | |
}; | |
Either.prototype.fork = function (f, g) { | |
return this.doFork.call(null, f, g); | |
}; | |
Either.prototype.of = function (value) { | |
return Either.of(value); | |
}; | |
Either.prototype.fail = function (error) { | |
return Either.fail(error); | |
}; | |
Either.prototype.empty = function () { | |
return Either.empty(); | |
}; | |
Either.prototype.getValue = function () { | |
return this.fork((function (v) { | |
return v; | |
}), (function (e) { | |
throw e; | |
})); | |
}; | |
Either.prototype.chain = function (m) { | |
var _this = this; | |
return new Either(function (f, g) { | |
return _this.fork((function (v) { | |
return m(v).fork(f, g); | |
}), g); | |
}); | |
}; | |
Either.prototype.map = function (m) { | |
var _this = this; | |
return this.chain(function (v) { | |
return _this.of(m(v)); | |
}); | |
}; | |
Either.prototype.concat = function (either) { | |
var _this = this; | |
return new Either(function (f, g) { | |
return _this.fork(f, function (err) { | |
return either.fork(f, g); | |
}); | |
}); | |
}; | |
return Either; | |
})(); | |
exports.Either = Either; | |
; | |
var Promise = (function () { | |
function Promise(doTap) { | |
this.doTap = doTap; | |
} | |
Promise.of = function (v) { | |
return new Promise(function (cb) { | |
cb(Either.of(v)); | |
}); | |
}; | |
Promise.fail = function (e) { | |
return new Promise(function (cb) { | |
cb(Either.fail(e)); | |
}); | |
}; | |
Promise.empty = function () { | |
return Promise.fail(null); | |
}; | |
Promise.prototype.of = function (v) { | |
return Promise.of(v); | |
}; | |
Promise.prototype.empty = function () { | |
return Promise.empty(); | |
}; | |
Promise.prototype.fail = function (e) { | |
return Promise.fail(e); | |
}; | |
Promise.prototype.tap = function (cb) { | |
window.setImmediate(function () { | |
this.doTap(cb); | |
}); | |
}; | |
Promise.prototype.map = function (f) { | |
return this.chain(function (v) { | |
return Promise.of(f(v)); | |
}); | |
}; | |
Promise.prototype.chain = function (f) { | |
var _this = this; | |
return new Promise(function (cb) { | |
return _this.tap(function (either) { | |
either.fork(function (v) { | |
f(v).tap(cb); | |
}, function (e) { | |
cb(Either.fail(e)); | |
}); | |
}); | |
}); | |
}; | |
Promise.prototype.concat = function (other) { | |
var _this = this; | |
return new Promise(function (cb) { | |
return _this.tap(function (either) { | |
either.fork(function (v) { | |
cb(Either.of(v)); | |
}, function (_) { | |
other.tap(cb); | |
}); | |
}); | |
}); | |
}; | |
return Promise; | |
})(); | |
exports.Promise = Promise; | |
function filterM(m, f) { | |
return m.chain(function (v) { | |
return (f(v)) ? m.of(v) : m.empty(); | |
}); | |
} | |
}); |
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
export interface Monad<T> { | |
chain<TT>(f : (v: T) => Monad<TT>) : Monad<TT>; | |
concat (m:Monad<T>) : Monad<T>; | |
empty<T>() : Monad<T>; | |
map<A, B>(f:(A) => B) : Monad<B>; | |
of<A>(v:A): Monad<A>; | |
} | |
export class Either<T, E> implements Monad<T> { | |
constructor (private doFork: (f: (value: T) => Object, | |
g: (error: E) => Object) => Object) { | |
} | |
static fail<A, F> (error: F) : Either<A, F> { | |
return new Either<A, F>((f, g) => g(error)); | |
} | |
static of<A, F> (value: A) : Either<A, F> { | |
return new Either<A, F>((f, g) => f(value)); | |
} | |
static empty<A, F> () : Either<A, F> { | |
return this.fail<A, F>(null); | |
} | |
fork<A> (f: (value: T) => A, g: (error: E) => A) : A { | |
return <A>this.doFork.call(null, f, g); | |
} | |
of<A, F> (value: A) : Either<A, F> { | |
return Either.of<A, F>(value); | |
} | |
fail<A, F> (error: F) : Either<A, F> { | |
return Either.fail<A, F>(error); | |
} | |
empty (): Either <T, E> { | |
return Either.empty<T, E>(); | |
} | |
getValue () : T { | |
return this.fork<T>( | |
((v) => v), | |
((e):T => { | |
throw e; | |
})); | |
} | |
chain<TT>(m: (value:T) => Either<TT, E>) : Either<TT, E> { | |
return new Either<TT, E>((f, g) => this.fork( | |
((v) => m(v).fork(f, g)), | |
g | |
)); | |
} | |
map<A>(m: (value: T) => A) : Either<A, E> { | |
return this.chain<A>((v) => this.of<A, E>(m(v))); | |
} | |
concat(either: Either<T, E>) : Either<T, E> { | |
return new Either<T, E>( | |
(f, g) => this.fork(f, (err) => either.fork(f, g)) | |
); | |
} | |
}; | |
export class Promise<V,E> implements Monad<V> { | |
constructor (private doTap: (cb: (e:Either<V, E>) => void) => void) {} | |
static of<VV, EE>(v: VV) : Promise<VV, EE> { | |
return new Promise<VV,EE>(function (cb) { | |
cb(Either.of<VV, EE>(v)) | |
}); | |
} | |
static fail<VV, EE>(e: EE) : Promise<VV, EE> { | |
return new Promise<VV, EE>(function (cb) { | |
cb(Either.fail<VV, EE>(e)); | |
}); | |
} | |
static empty<VV, EE> () : Promise<VV, EE> { | |
return Promise.fail<VV, EE>(null); | |
} | |
of<VV, EE>(v: VV) : Promise<VV, EE> { | |
return Promise.of<VV, EE>(v); | |
} | |
empty<VV, EE>() : Promise<VV, EE> { | |
return Promise.empty<VV, EE>(); | |
} | |
fail<VV, EE>(e:EE) : Promise<VV, EE> { | |
return Promise.fail<VV, EE>(e); | |
} | |
tap (cb:(e:Either<V,E>) => void):void { | |
window.setImmediate(function () { this.doTap(cb) }); | |
} | |
map<VV> (f:(_:V) => VV): Promise<VV, E> { | |
return this.chain((v) => Promise.of<VV, E>(f(v))); | |
} | |
chain<VV> (f:(_:V) => Promise<VV, E>) { | |
return new Promise<VV, E>((cb) => this.tap(function (either:Either<V, E>):void { | |
either.fork( | |
function (v) { f(v).tap(cb) }, | |
function (e) { cb(Either.fail<VV, E>(e)) }); | |
})); | |
} | |
concat (other: Promise<V, E>) { | |
return new Promise<V, E>((cb) => this.tap(function (either) { | |
either.fork( | |
function (v) { cb(Either.of<V, E>(v)) }, | |
function (_) { other.tap(cb) }); | |
})); | |
} | |
} | |
function filterM<A>(m:Monad<A>, f:(v:A) => boolean):Monad<A> { | |
return m.chain((v) => (f(v)) ? m.of(v) : m.empty()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment