Skip to content

Instantly share code, notes, and snippets.

@jcouyang
Last active October 25, 2017 02:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcouyang/b4622a817708a41a709e9018225bd376 to your computer and use it in GitHub Desktop.
Save jcouyang/b4622a817708a41a709e9018225bd376 to your computer and use it in GitHub Desktop.
// 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