Skip to content

Instantly share code, notes, and snippets.

@wernerdegroot
Last active January 20, 2019 16:00
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 wernerdegroot/89a7efaf4b975a6f1416a76482a53a90 to your computer and use it in GitHub Desktop.
Save wernerdegroot/89a7efaf4b975a6f1416a76482a53a90 to your computer and use it in GitHub Desktop.
Experiments with capturing a generic continuation as a Promise
type Hkts<A> = {
Id: A
Array: A[]
}
type HktToken = keyof Hkts<any>
type Continuation<T extends HktToken, A> = <B>(c: (a: A) => Hkts<B>[T]) => Hkts<B>[T]
type FlatMap<T extends HktToken> = <A, B>(fa: Hkts<A>[T], fn: (a: A) => Hkts<B>[T]) => Hkts<B>[T]
const Continuations = {
pure<T extends HktToken, A>(a: A): Continuation<T, A> {
return c => c(a)
},
map<T extends HktToken, A, B>(ca: Continuation<T, A>, fn: (a: A) => B): Continuation<T, B> {
return Continuations.flatMap(ca, a => {
const b = fn(a)
return Continuations.pure<T, B>(b)
})
},
flatMap<T extends HktToken, A, B>(ca: Continuation<T, A>, fn: (a: A) => Continuation<T, B>): Continuation<T, B> {
return <C>(c: (b: B) => Hkts<C>[T]): Hkts<C>[T] => {
return ca((a: A) => {
return fn(a)(c)
})
}
}
}
const Arrays = {
flatten<A>(ar: A[][]): A[] {
return ar.reduce((acc, curr) => {
acc.push(...curr)
return acc
}, [])
},
flatMap<A, B>(ar: A[], fn: (a: A) => B[]): B[] {
return Arrays.flatten(ar.map(fn))
}
}
const plus = <T extends 'Array'>(left: number, right: number): Continuation<T, number> => {
return c => Arrays.flatMap([left + right, left - right], c)
}
const half = <T extends 'Array'>(n: number): Continuation<T, number> => {
return c => {
return Arrays.flatMap([n / 2, n / 4], c)
}
}
const idealPlus = (left: number, right: number) => {
return new IdealContinuationPromise(plus(left, right))
}
const idealHalf = (n: number) => {
return new IdealContinuationPromise(half(n))
}
class IdealContinuationPromise<T extends HktToken, A> {
constructor(public c: Continuation<T, A>) {
}
map<B>(fn: (a: A) => B): IdealContinuationPromise<T, B> {
const c = Continuations.map(this.c, fn)
return new IdealContinuationPromise(c)
}
flatMap<B>(fn: (a: A) => IdealContinuationPromise<T, B>) {
const c = Continuations.flatMap<T, A, B>(this.c, a => fn(a).c)
return new IdealContinuationPromise(c)
}
}
const fn = (resolve: (n: number) => void): void => {
resolve(10)
resolve(20)
}
const fnc: Continuation<'Array', number> = <B>(resolve: (n: number) => B[]): B[] => {
const calledWith: number[] = []
fn((n: number) => {
calledWith.push(n)
})
return Arrays.flatMap(calledWith, resolve)
}
let counter = 0
class MyPromise<A> {
c: Continuation<'Array', A>
constructor(c: Continuation<'Array', A>, flag: 'theRealThing', rest?: string)
constructor(executor: (resolve: (value?: A | PromiseLike<A>) => void, reject: (reason?: any) => void) => void)
constructor(p: ((resolve: (value?: A | PromiseLike<A>) => void, reject: (reason?: any) => void) => void) | Continuation<'Array', A>, flag?: 'theRealThing', rest?: string) {
const id = rest || String(counter++)
debugger
console.log('constructor', id)
if (flag === 'theRealThing') {
console.log(id, 'is real')
this.c = p as Continuation<'Array', A>
} else {
console.log(id, 'needs converting')
const executor = p as (resolve: (value?: A | PromiseLike<A>) => void, reject: (reason?: any) => void) => void
const calledWith: A[] = []
executor(
(a?: A | PromiseLike<A>) => {
if (a === undefined) {
throw new Error()
}
if (a instanceof MyPromise) {
calledWith.push(...a.c(x => {
console.log('Adding to called with', x, id)
return [x]
}))
return
}
if (typeof a === 'object' && a['then'] !== undefined) {
throw new Error()
}
console.log('Called with', a, id)
calledWith.push(a as A)
},
reason => {
throw new Error(reason)
}
)
console.log('called with 1', calledWith, id)
this.c = <B>(resolve: (a: A) => B[]): B[] => {
console.log('called with 2', calledWith, id)
return Arrays.flatMap(calledWith, resolve)
}
}
}
then<TResult1 = A, TResult2 = never>(onfulfilled?: ((value: A) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): MyPromise<TResult1 | TResult2> {
console.log('then')
if (onfulfilled === undefined || onfulfilled === null) {
console.log('What')
onrejected && onrejected('FAIL')
throw new Error()
}
const c = <C>(c: (b: TResult1) => C[]): C[] => {
return this.c((a: A) => {
const promiseLike = onfulfilled(a)
if (promiseLike instanceof MyPromise) {
return promiseLike.c(c)
} else {
throw new Error()
}
})
}
return new MyPromise<TResult1>(c, 'theRealThing')
}
}
async function doIt(): MyPromise<number> {
const n = await new MyPromise(fnc, 'theRealThing', 'n')
console.log('n', n)
const h = await new MyPromise(half(n), 'theRealThing', 'half n')
return h
}
doIt().c(x => {
console.log(x)
return [4]
})
counter = 0
console.log('Start again')
new MyPromise((resolve, reject) => {
new MyPromise(fnc, 'theRealThing', 'n')
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment