Last active
October 25, 2017 02:22
-
-
Save jcouyang/b4622a817708a41a709e9018225bd376 to your computer and use it in GitHub Desktop.
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
// Higher Kind Type dictionary | |
interface _<A> { } | |
type HKT = keyof _<any> | |
type $<F extends HKT, A> = _<A>[F] | |
// Functor Type Class | |
interface Functor<F extends HKT> { | |
map<A, B>(f: (a: A) => B, fa: $<F, A>): $<F, B> | |
} | |
// Datatype Tag | |
function datatype(name: string) { | |
return (constructor: Function) => { | |
Reflect.defineMetadata('design:type', name, constructor); | |
} | |
} | |
function kind(target: any) { | |
return Reflect.getMetadata('design:type', target.constructor); | |
} | |
// Xstream Datatype | |
@datatype('Xstream') | |
class Xstream<A> { | |
value: A | |
constructor(v: A) { | |
this.value = v | |
} | |
} | |
// declare type of Xstream as HKT | |
interface _<A> { | |
"Xstream": Xstream<A> | |
} | |
// declare Functor instance of Xstream | |
class XstreamFunctor implements Functor<"Xstream"> { | |
map<A, B>(f: (v: A) => B, fa: Xstream<A>): Xstream<B> { | |
return new Xstream(f(fa.value)) | |
} | |
} | |
@datatype('Ystream') | |
class Ystream<T> { | |
value: T | |
constructor(v: T) { | |
this.value = v | |
} | |
} | |
interface _<A> { | |
"Ystream": Ystream<A> | |
} | |
class YstreamFunctor implements Functor<"Ystream"> { | |
map<A, B>(f: (v: A) => B, fa: Ystream<A>): Ystream<B> { | |
return new Ystream(f(fa.value)) | |
} | |
} | |
// add instances into Functor dictionary | |
namespace Functor { | |
export const Xstream = new XstreamFunctor | |
export const Ystream = new YstreamFunctor | |
} | |
type FunctorInstance = keyof typeof Functor | |
// Polymorphic map function | |
function map<F extends FunctorInstance, A, B>(f: (v: A) => B, fa: $<F, A>): $<F, B> { | |
return (<any>Functor[fa.constructor.name as F]).map(f, fa) as $<F, B> | |
} | |
map<"Xstream", number, number>(a => a + 1, new Xstream(1)) | |
map<"Ystream", number, number>(a => a + 1, new Ystream(1)) | |
// Cartesian | |
type CartesianInstances = keyof typeof Cartesian | |
interface Cartesian<F extends HKT> { | |
product<A, B>(fa: $<F, A>, fb: $<F, B>): $<F, [A, B]> | |
} | |
namespace Cartesian { | |
export let Xstream: Cartesian<"Xstream"> | |
} | |
function product<F extends CartesianInstances, A, B>(fa: $<F, A>, fb: $<F, B>): $<F, [A, B]> { | |
let instance = (<any>Cartesian)[kind(fa)] | |
return instance.product(fa, fb) as $<F, [A, B]> | |
} | |
class XstreamCartesian implements Cartesian<"Xstream"> { | |
product<A, B>(fa: Xstream<A>, fb: Xstream<B>): Xstream<[A, B]> { | |
return new Xstream([fa.value, fb.value] as [A, B]) | |
} | |
} | |
Cartesian.Xstream = new XstreamCartesian | |
product<"Xstream", number, number>(new Xstream(1), new Xstream(2)) | |
// Apply TypeClass | |
interface Apply<F extends HKT> extends Cartesian<F>, Functor<F> { | |
ap<A, B>(fab: $<F, (a: A) => B>, fa: $<F, A>): $<F, B> | |
} | |
type ApplyInstances = keyof typeof Apply | |
namespace Apply { | |
export let Xstream: Apply<"Xstream"> | |
} | |
function ap<F extends ApplyInstances, A, B>(fab: $<F, (a: A) => B>, fa: $<F, A>): $<F, B> { | |
let instance = (<any>Functor)[kind(fab)] | |
return instance.ap(fab, fa) as $<F, B> | |
} | |
class XstreamApply implements Apply<"Xstream"> { | |
ap<A, B>(fab: Xstream<(a: A) => B>, fa: Xstream<A>): Xstream<B> { | |
return new Xstream(fab.value(fa.value)) | |
} | |
map = Functor.Xstream.map | |
product = Cartesian.Xstream.product | |
} | |
function ap2<F extends ApplyInstances, A, B, C>(fabc: $<F, (a: A, b: B) => C>, fa: $<F, A>, fb: $<F, B>): $<F, C> { | |
let instance: any = Apply[kind(fabc) as F] | |
return instance.ap( | |
instance.map( | |
(f: (a: A, b: B) => C) => (([a, b]: [A, B]) => f(a, b)) | |
, fabc) | |
, instance.product(fa, fb) | |
) as $<F, C> | |
} | |
ap2<"Xstream", number, number, number>( | |
new Xstream((a: number, b: number) => a + b), | |
new Xstream(2), | |
new Xstream(3) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment