From 06f64c66f29e9a08f8cd387ba736d3cefa93b449 Mon Sep 17 00:00:00 2001 From: Henri Binsztok <808274+hbbio@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:34:03 +0800 Subject: [PATCH 1/4] array: use mapNoPrevious in reduce --- src/array.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/array.ts b/src/array.ts index 0687a79..43ab431 100644 --- a/src/array.ts +++ b/src/array.ts @@ -183,9 +183,9 @@ export const reduce = < nf?: NF ): MapCell => { const coll = collector>(proxy); - return proxy.map( + return proxy.mapNoPrevious( [arr], - (cells) => + async (cells) => coll( proxy.mapNoPrevious( cells, From d873aa8c38fa17abe925d5a9e998d451c334b6f3 Mon Sep 17 00:00:00 2001 From: Henri Binsztok <808274+hbbio@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:34:22 +0800 Subject: [PATCH 2/4] cell: additional subscribers tests --- src/cell.test.ts | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/cell.test.ts b/src/cell.test.ts index 609a285..859cf78 100644 --- a/src/cell.test.ts +++ b/src/cell.test.ts @@ -2,6 +2,9 @@ import { describe, expect, it, test } from "vitest"; import { isEqual } from "./isEqual.test"; +import { reduce } from "./array"; +import { cellify } from "./cellify"; +import { SheetProxy } from "./proxy"; import { Sheet } from "./sheet"; describe("writable", () => { @@ -241,3 +244,54 @@ test("subscribe doesn't return undefined", async () => { url.set("https://images.dog.ceo/breeds/frise-bichon/1.jpg"); expect(l).toEqual(["frise-bichon"]); // cspell:disable-line }); + +test("subscriber keeps working when pointer is updated", async () => { + const sheet = new Sheet(); + const proxy = new SheetProxy(sheet); + const a = proxy.new(1, "a"); + let sum = 0; + a.subscribe((v) => { + sum += v; + }); + expect(sum).toBe(1); + const b = proxy.new(2, "b"); + expect(sum).toBe(1); // unchanged + a.set(b); + expect(sum).toBe(3); // b + b.set(3); + expect(sum).toBe(6); // b update +}); + +test("subscriber keeps working when pointer is updated with pointer chain", async () => { + const sheet = new Sheet(); + const proxy = new SheetProxy(sheet); + const a = proxy.new(1, "a"); + let sum = 0; + a.subscribe((v) => { + sum += v; + }); + expect(sum).toBe(1); + const b = proxy.new(2, "b"); + const c = proxy.new(b, "c"); + expect(sum).toBe(1); // unchanged + a.set(c); + expect(sum).toBe(3); // b + b.set(3); + expect(sum).toBe(6); // b update +}); + +test("subscriber keeps working with mapped pointers", async () => { + const sheet = new Sheet(); + const proxy = new SheetProxy(sheet); + const one = proxy.new(1, "one"); + const arr = cellify(proxy, [one, 2, 3]); + const sum = reduce(proxy, arr, (acc, elt) => acc + elt, 0); + let count = 0; + sum.subscribe((v) => count++); + await proxy.working.wait(); + expect(count).toBe(1); + one.set(2); + expect(count).toBe(2); + arr.update((arr) => [...arr, proxy.new(4, "4")]); + expect(count).toBe(3); +}); From 63d2c1e7a74bfd1ae957dfbdd1424e41bd2b3d17 Mon Sep 17 00:00:00 2001 From: Henri Binsztok <808274+hbbio@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:35:10 +0800 Subject: [PATCH 3/4] cellify: update Cellified type with existing cells --- src/cellify.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/cellify.ts b/src/cellify.ts index 08b8d03..0db0220 100644 --- a/src/cellify.ts +++ b/src/cellify.ts @@ -3,13 +3,15 @@ import { collector } from "./gc"; import type { SheetProxy } from "./proxy"; // Cellified computes a cellified type. -export type Cellified = T extends object - ? T extends Array - ? ValueCell[]> - : ValueCell<{ - [P in keyof T]: Cellified; - }> - : ValueCell; +export type Cellified = T extends AnyCell + ? AnyCell + : T extends object + ? T extends Array + ? ValueCell[]> + : ValueCell<{ + [P in keyof T]: Cellified; + }> + : ValueCell; // Uncellified computes an uncellified type. export type Uncellified = T extends AnyCell From 3c8e8e4b6d05f7e3ba9ac9d8617bfa0178cf216c Mon Sep 17 00:00:00 2001 From: Henri Binsztok <808274+hbbio@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:35:56 +0800 Subject: [PATCH 4/4] sheet, cell: Computations are Record --- src/cell.ts | 22 ++++++++++++++-------- src/sheet.ts | 18 +++++++++--------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/cell.ts b/src/cell.ts index 75d084e..3039b75 100644 --- a/src/cell.ts +++ b/src/cell.ts @@ -47,10 +47,10 @@ export type PendingArray = Promise< >; /** Array of (maybe pending) computation results */ -export type MaybePendingDict = ( - | PendingMaybe - | MaybeResultOrPointer -)[]; +export type MaybePendingDict = Record< + number, + PendingMaybe | MaybeResultOrPointer +>; export type Not = T extends true ? false : true; @@ -97,7 +97,10 @@ abstract class SubscribeBench { dispatch( value, (value) => { - if (value instanceof Canceled) return; + if (value instanceof Canceled) { + console.log("canceled", value); + return; + } // if their is no value, wait for the // next update for the first notification if (value !== undefined) { @@ -895,9 +898,12 @@ export class MapCell extends Cell { * @param provided dependencies provided by the sheet as their computation have been triggered before. * @returns The list of promise of values for all dependencies (in the order of the dependencies) */ - private _gatherDependencies(provided: { - [key: number]: PendingMaybe | MaybeResultOrPointer; - }): PendingArray | MaybeResultOrPointer[] { + private _gatherDependencies( + provided: Record< + number, + PendingMaybe | MaybeResultOrPointer + > + ): PendingArray | MaybeResultOrPointer[] { const deps: (PendingMaybe | V)[] = []; this.sheet.debug([this.id], "gathering deps values", { id: this.id, diff --git a/src/sheet.ts b/src/sheet.ts index c87b6e7..1b60443 100644 --- a/src/sheet.ts +++ b/src/sheet.ts @@ -17,10 +17,10 @@ import { dispatch, dispatchPromiseOrValueArray } from "./promise"; import { SheetProxy } from "./proxy"; import type { AnyCellArray } from "./types"; -type Computations = ( - | Pending - | CellResult -)[]; +type Computations = Record< + number, + Pending | CellResult +>; type IterationResult = { computations: Computations; updated: Set; @@ -545,7 +545,7 @@ export class Sheet { ); }; - const computations: Computations = []; + const computations: Computations = {}; const release = this.working.startNewComputation(); for (const id of roots) { computations[id] = dispatch( @@ -668,7 +668,7 @@ export class Sheet { // Form now on, we need updatable pointers to be up-to-date to continue const newComputations = this.computeUpdatable(toBeRecomputed, computations); const borderComputation = dispatchPromiseOrValueArray( - newComputations, + Object.values(newComputations), // wait for all new computations to be over before proceeding to the next step (newComputations) => { // finding all canceled computations @@ -700,7 +700,7 @@ export class Sheet { borderComputation, ({ computationsOfBorder, nextIteration }) => { return dispatchPromiseOrValueArray( - computationsOfBorder, + Object.values(computationsOfBorder), (computationsOfBorder) => { //checking for canceled computations this.registerCancelAndDone( @@ -797,7 +797,7 @@ export class Sheet { return res; } - private dependentCells(id) { + private dependentCells(id: number) { return Array.from( new Set([...(this.g.get(id) || []), ...this._pointers.get(id)]) ); @@ -879,7 +879,7 @@ export class Sheet { ): Computations { const order = toBeRecomputed.slice(); // slice copies the array let currentCellId: number | undefined; - const newComputations = []; + const newComputations = {}; // biome-ignore lint/suspicious/noAssignInExpressions: shorter, still explicit while ((currentCellId = order.pop()) !== undefined) {