Skip to content

Instantly share code, notes, and snippets.

@maxgherman
Created June 26, 2018 05:13
Show Gist options
  • Save maxgherman/01c129d07a13d8118a0d24387ce2a136 to your computer and use it in GitHub Desktop.
Save maxgherman/01c129d07a13d8118a0d24387ce2a136 to your computer and use it in GitHub Desktop.
Typescript functional computations
class Box<F, T> { }
class IsArray {}
interface IFunctor<F> {
fmap<A, B>(f: (a: A) => B, fa: Box<F, A>) : Box<F, B>;
}
type ArrayF<T> = Box<IsArray, T> & Array<T>;
class BoxedArray<K, T extends Array<K>> extends Box<IsArray, T> {
private constructor(private value: T) {
super();
this.value = value || [];
}
public static wrap<K, T extends Array<K>>(value: T) {
return new BoxedArray<K, T>(value);
}
public unwrap(): T {
return this.value;
}
}
class PlainArrayFunctor implements IFunctor<IsArray> {
fmap<A, B>(f: (a: A) => B, fa: ArrayF<A>) : ArrayF<B> {
return fa.map(f);
}
}
class BoxedArrayFunctor<A, B> implements IFunctor<IsArray> {
fmap<A, B>(f: (a: A) => B, fa: BoxedArray<A, A[]>) : BoxedArray<B, B[]> {
return BoxedArray.wrap(fa.unwrap().map(f));
}
}
class Maybe<T> {
private _isNothing: boolean;
private constructor(private _value?: T) {
this._isNothing = _value ? false : true;
}
public static from<R>(value?: R) {
return new Maybe<R>(value);
}
public static Nothing<R>() {
return new Maybe<R>();
}
public get isJust() {
return !this.isNothing;
}
public get isNothing() {
return this._isNothing;
}
public get value() {
return this._value;
}
}
class IsMaybe {}
type MaybeF<T> = Box<IsMaybe, T> & Maybe<T>;
class MaybeFunctor implements IFunctor<IsMaybe> {
fmap = <A, B>(f: (a: A) => B, fa: MaybeF<A>) : MaybeF<B> =>
fa.isNothing ? Maybe.Nothing() : Maybe.from(f(fa.value as A));
}
class Either<T1, T2> {
private _isLeft: boolean;
private _isRight: boolean;
private constructor(private _left?: T1, private _right?: T2) {
this._isLeft = _left ? true: false;
this._isRight = _right ? true: false;
}
public static Left<T>(a: T) {
return new Either<T, {}>(a);
}
public static Right<T>(a: T) {
return new Either<{}, T>(undefined, a);
}
public get isLeft() {
return this._isLeft;
}
public get isRight() {
return this._isRight;
}
public get value() {
return this._isLeft ? this._left : this._right;
}
}
class IsEither {}
type EitherF<T1, T2> = Box<IsEither, T2> & Either<T1, T2>;
class EitherFunctor<R> implements IFunctor<IsEither> {
fmap = <A, B>(f: (a: A) => B, fa: EitherF<R, A>) : EitherF<R, B> =>
fa.isLeft ?
Either.Left(fa.value) as EitherF<R, B> :
Either.Right(f(fa.value as A)) as EitherF<R, B>;
}
const apply = (f, g) => (...args) => f(g(...args));
const compose = (...fns: Function[]) => fns.reduce(apply);
const papply = (f, a) => b => f(a, b)
/// ------------------------------------------------------------------
const arrayF = new PlainArrayFunctor();
const arrayFResult = arrayF.fmap((item: number) => item * 2, [1, 2, 3]);
const arrayBoxed = new BoxedArrayFunctor();
const arrayBResult = arrayBoxed.fmap((item) => item * 2, BoxedArray.wrap<number, number[]>([1, 2, 3]));
const maybeF = new MaybeFunctor();
const maybeFResult = maybeF.fmap((item) => item * 2, Maybe.from(123));
const eitherF = new EitherFunctor();
const eitherFResult = eitherF.fmap((item) => item * 2, Either.Right(123));
// console.log('arrayF ', arrayFResult);
// console.log('arrayB ', arrayBResult);
// console.log('maybeF ', maybeFResult);
// console.log('eitherF ', eitherFResult);
const maybeMapper1 = papply(maybeF.fmap, (item) => item * 2);
const maybeMapper2 = papply(maybeF.fmap, (item) => item + 7.5);
const maybeMapper3 = papply(maybeF.fmap, (item) => Math.pow(item, 0.5));
const logger = (item) => { console.log(item); return item };
const composer = compose(
maybeMapper3,
logger,
maybeMapper1,
logger,
maybeMapper2
);
console.log(composer(Maybe.from(5)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment