changeset 2274:df9a6c457587

convert Patch to use Immutable
author drewp@bigasterisk.com
date Mon, 29 May 2023 15:15:19 -0700
parents 4074dbec5c46
children dd9474bef2a6
files light9/web/patch.test.ts light9/web/patch.ts package.json pnpm-lock.yaml
diffstat 4 files changed, 75 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/light9/web/patch.test.ts	Mon May 29 13:58:04 2023 -0700
+++ b/light9/web/patch.test.ts	Mon May 29 15:15:19 2023 -0700
@@ -43,3 +43,13 @@
     assert.isTrue(new Patch([new Quad(node1, node2, node3)], []).matches(QP(null, null, node3, null)));
   });
 });
+describe("Patch.empty", () => {
+  it("works with no quads", () => {
+    const p = new Patch([], []);
+    assert.isTrue(p.isEmpty());
+  });
+  it("works with unmatched quads", () => {
+    const p = new Patch([], [new Quad(node1, node2, node3)]);
+    assert.isFalse(p.isEmpty());
+  });
+});
--- a/light9/web/patch.ts	Mon May 29 13:58:04 2023 -0700
+++ b/light9/web/patch.ts	Mon May 29 15:15:19 2023 -0700
@@ -2,7 +2,7 @@
 import debug from "debug";
 import * as N3 from "n3";
 import { NamedNode, Parser, Quad, Writer } from "n3";
-
+import * as Immutable from "immutable";
 export interface QuadPattern {
   subject: N3.Quad_Subject | null;
   predicate: N3.Quad_Predicate | null;
@@ -14,29 +14,32 @@
 
 export class Patch {
   // immutable
-  private _allPredsCache?: Set<string>;
-  private _allSubjsCache?: Set<string>;
-  constructor(private dels: Quad[], private adds: Quad[]) {
+  private dels: Immutable.Set<Quad>;
+  private adds: Immutable.Set<Quad>;
+  private _allPredsCache?: Immutable.Set<string>;
+  private _allSubjsCache?: Immutable.Set<string>;
+  constructor(dels: Iterable<Quad>, adds: Iterable<Quad>) {
+    this.dels = Immutable.Set(dels);
+    this.adds = Immutable.Set(adds);
     this.validate();
   }
+
   private validate() {
     // todo: finish porting this from coffeescript
-    [this.adds, this.dels].map((qs: Quad[]) =>
-      (() => {
-        const result = [];
-        for (let q of Array.from(qs)) {
-          if (!q.equals) {
-            throw new Error("doesn't look like a proper Quad");
-          }
-          if (!q.subject.id || q.graph.id == null || q.predicate.id == null) {
-            throw new Error(`corrupt patch: ${JSON.stringify(q)}`);
-          } else {
-            result.push(undefined);
-          }
-        }
-        return result;
-      })()
-    );
+    this.adds.union(this.dels).forEach((q: Quad) => {
+      if (!q.equals) {
+        throw new Error("doesn't look like a proper Quad");
+      }
+      if (!q.subject.id || q.graph.id == null || q.predicate.id == null) {
+        throw new Error(`corrupt patch: ${JSON.stringify(q)}`);
+      }
+      if (
+        q.object.termType == "Literal" &&
+        (q.object.datatypeString == "http://www.w3.org/2001/XMLSchema#float" || q.object.datatypeString == "http://www.w3.org/2001/XMLSchema#double")
+      ) {
+        throw new Error(`${JSON.stringify(q)} is using non-decimal for numbers, which is going to break some comparisons`);
+      }
+    });
   }
 
   matches(pat: QuadPattern): boolean {
@@ -52,26 +55,25 @@
   }
 
   isEmpty() {
-    // sometimes returns bogus false- waiting for port to Immutable
-    return !this.dels.length && !this.adds.length;
+    return Immutable.is(this.dels, this.adds);
   }
 
   applyToGraph(g: N3.Store) {
-    for (let quad of Array.from(this.dels)) {
+    for (let quad of this.dels) {
       g.removeQuad(quad);
     }
-    for (let quad of Array.from(this.adds)) {
+    for (let quad of this.adds) {
       g.addQuad(quad);
     }
   }
 
   update(other: Patch): Patch {
-    // this is approx, since it doesnt handle matching existing quads.
-    return new Patch(this.dels.concat(other.dels), this.adds.concat(other.adds));
+    // this is approx, since it doesnt handle cancelling existing quads.
+    return new Patch(this.dels.union(other.dels), this.adds.union(other.adds));
   }
 
   summary(): string {
-    return "-" + this.dels.length + " +" + this.adds.length;
+    return "-" + this.dels.size + " +" + this.adds.size;
   }
 
   dump(): string {
@@ -101,7 +103,7 @@
 
       const writeDels = (cb1: () => void) => {
         const writer = new Writer({ format: "N-Quads" });
-        writer.addQuads(this.dels);
+        writer.addQuads(this.dels.toArray());
         writer.end(function (err: any, result: string) {
           out.patch.deletes = result;
           cb1();
@@ -110,7 +112,7 @@
 
       const writeAdds = (cb2: () => void) => {
         const writer = new Writer({ format: "N-Quads" });
-        writer.addQuads(this.adds);
+        writer.addQuads(this.adds.toArray());
         writer.end(function (err: any, result: string) {
           out.patch.adds = result;
           cb2();
@@ -121,17 +123,19 @@
     });
   }
 
-  containsAnyPreds(preds: NamedNode[]): boolean {
+  containsAnyPreds(preds: Iterable<NamedNode>): boolean {
     if (this._allPredsCache === undefined) {
-      this._allPredsCache = new Set();
-      for (let qq of [this.adds, this.dels]) {
-        for (let q of Array.from(qq)) {
-          this._allPredsCache.add(q.predicate.value);
+      this._allPredsCache = Immutable.Set();
+      this._allPredsCache.withMutations((cache) => {
+        for (let qq of [this.adds, this.dels]) {
+          for (let q of Array.from(qq)) {
+            cache.add(q.predicate.value);
+          }
         }
-      }
+      });
     }
 
-    for (let p of Array.from(preds)) {
+    for (let p of preds) {
       if (this._allPredsCache.has(p.value)) {
         return true;
       }
@@ -139,28 +143,33 @@
     return false;
   }
 
-  allSubjs(): Set<string> {
+  allSubjs(): Immutable.Set<string> {
     // returns subjs as Set of strings
     if (this._allSubjsCache === undefined) {
-      this._allSubjsCache = new Set();
-      for (let qq of [this.adds, this.dels]) {
-        for (let q of Array.from(qq)) {
-          this._allSubjsCache.add(q.subject.value);
+      this._allSubjsCache = Immutable.Set();
+      this._allSubjsCache.withMutations((cache) => {
+        for (let qq of [this.adds, this.dels]) {
+          for (let q of Array.from(qq)) {
+            cache.add(q.subject.value);
+          }
         }
-      }
+      });
     }
 
     return this._allSubjsCache;
   }
 
-  allPreds(): Set<NamedNode> {
-    const ret = new Set<NamedNode>();
-    for (let qq of [this.adds, this.dels]) {
-      for (let q of Array.from(qq)) {
-        if (q.predicate.termType == "Variable") throw "unsupported";
-        ret.add(q.predicate);
+  allPreds(): Immutable.Set<NamedNode> {
+    // todo: this could cache
+    const ret = Immutable.Set<NamedNode>();
+    ret.withMutations((r) => {
+      for (let qq of [this.adds, this.dels]) {
+        for (let q of Array.from(qq)) {
+          if (q.predicate.termType == "Variable") throw "unsupported";
+          r.add(q.predicate);
+        }
       }
-    }
+    });
     return ret;
   }
 }
--- a/package.json	Mon May 29 13:58:04 2023 -0700
+++ b/package.json	Mon May 29 15:15:19 2023 -0700
@@ -23,6 +23,7 @@
     "d3": "^7.8.4",
     "debug": "^4.3.4",
     "fpsmeter": "^0.3.1",
+    "immutable": "^4.3.0",
     "knockout": "^3.5.1",
     "lit": "^2.7.4",
     "n3": "^1.16.4",
--- a/pnpm-lock.yaml	Mon May 29 13:58:04 2023 -0700
+++ b/pnpm-lock.yaml	Mon May 29 15:15:19 2023 -0700
@@ -15,6 +15,7 @@
   d3: ^7.8.4
   debug: ^4.3.4
   fpsmeter: ^0.3.1
+  immutable: ^4.3.0
   knockout: ^3.5.1
   lit: ^2.7.4
   n3: ^1.16.4
@@ -44,6 +45,7 @@
   d3: 7.8.4
   debug: 4.3.4
   fpsmeter: 0.3.1
+  immutable: 4.3.0
   knockout: 3.5.1
   lit: 2.7.4
   n3: 1.16.4
@@ -1156,6 +1158,10 @@
     resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
     dev: false
 
+  /immutable/4.3.0:
+    resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==}
+    dev: false
+
   /internmap/2.0.3:
     resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
     engines: {node: '>=12'}