Skip to content

Instantly share code, notes, and snippets.

@gatlin
Last active May 10, 2021 22:42
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 gatlin/48ee295e0071086f4cb1e53ee21a042c to your computer and use it in GitHub Desktop.
Save gatlin/48ee295e0071086f4cb1e53ee21a042c to your computer and use it in GitHub Desktop.
something relating reactive UIs and spreadsheets
/**
* 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