Last active
May 10, 2021 22:42
-
-
Save gatlin/48ee295e0071086f4cb1e53ee21a042c to your computer and use it in GitHub Desktop.
something relating reactive UIs and spreadsheets
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
/** | |
* outputs: | |
* a { a: 3, b: 6, c: 12 } | |
* b { a: 5, b: 10, c: 18 } | |
* c { a: 8, b: 16, c: 27 } | |
* { a: '25', b: '196', c: '900' } | |
* c 900 | |
*/ | |
// todo: use type system to statically verify the key exists in the object. | |
type PointedObject<S extends string, T> = { [key:string]: T }; | |
class Ctx<A> { | |
constructor( | |
public readonly key: string, | |
protected sheet: PointedObject<typeof key,A> | |
) { | |
if (! (key in sheet)) { | |
throw new Error( | |
`key ${key} does not exist in the properties object`); | |
} | |
const pds = Object.getOwnPropertyDescriptors(this); | |
Object.defineProperties(this, { | |
sheet: { ...pds.sheet, enumerable: false, writable: true } | |
}); | |
} | |
get value() { | |
return this.extract(); | |
} | |
get plain(): Record<string,A> { | |
return { ...this.sheet }; | |
} | |
public map<B>(f: (a: A) => B): Ctx<B> { | |
const key = this.key; | |
const result : Partial<PointedObject<typeof key,B>> = {}; | |
for (const [k,v] of Object.entries(this.sheet)) { | |
Object.defineProperty(result, k, { | |
enumerable: true, | |
writable: true, | |
value: f(v) | |
}); | |
} | |
return new Ctx(this.key, result as PointedObject<typeof key,B>); | |
} | |
public extract(): A { | |
return this.sheet[this.key]; | |
} | |
public duplicate(): Ctx<Ctx<A>> { | |
const key = this.key; | |
const sb: Partial<PointedObject<typeof key,Ctx<A>>> = {}; | |
for (const [k,v] of Object.entries(this.sheet)) { | |
Object.defineProperty(sb, k, { | |
enumerable: true, | |
value: new Ctx(k, this.sheet) | |
}); | |
} | |
return new Ctx(this.key, sb as PointedObject<typeof key,Ctx<A>>); | |
} | |
public cothen<B>(fn: (c: Ctx<A>) => B): Ctx<B> { | |
return this.duplicate().map(fn); | |
} | |
public apply<B>(fn: Ctx<(a: A) => B>): Ctx<B> { | |
const key = this.key; | |
const sb: Partial<PointedObject<typeof key,B>> = {}; | |
for (const [k,v] of Object.entries(this.sheet)) { | |
Object.defineProperty(sb, k, { | |
enumerable: true, | |
writable: true, | |
value: fn.plain[k](v) | |
}); | |
} | |
return new Ctx(this.key, sb as PointedObject<typeof key,B>); | |
} | |
} | |
///// | |
// demonstration follows | |
///// | |
// number context | |
const ctx1 : Ctx<number> = new Ctx("a",{ | |
a: 1, | |
get b() { | |
return this.b = this.a + this.a++; | |
}, | |
get c() { | |
return this.c = this.a + this.b; | |
} | |
}); | |
// applies an arbitrary transformation to each property. | |
const ctx2 : Ctx<string> = ctx1 | |
.cothen(({ key, plain, value }) => { | |
console.log(key, plain); | |
return value; | |
}) | |
.cothen(({ value }) => value * value) | |
.cothen(({ value }) => value.toString()); | |
console.log( | |
ctx2.plain | |
); | |
// safely change the key. | |
const ctx3 : Ctx<string> = ctx2.duplicate().plain.c; | |
console.log( | |
ctx3.key, | |
ctx3.value | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment