Split out dataspace.ts; more notes on patterns
This commit is contained in:
parent
55cb767635
commit
99e6b6ab3d
|
@ -0,0 +1,78 @@
|
|||
import { Assertion, Entity, Handle, Ref, Turn } from 'actor';
|
||||
import { Dictionary, IdentityMap, is, Record } from 'preserves';
|
||||
import { Bag, ChangeDescription } from './bag';
|
||||
|
||||
// Q. Why keep "Observe"? Why not do the clever trick of asserting the
|
||||
// observer, and having the dataspace read the implicit pattern it's
|
||||
// interested in off its attenuator?
|
||||
//
|
||||
// A. (1) Because we want to have the possibility of more than one
|
||||
// variety of pattern language. For example, here we have a simple
|
||||
// label match, but we'll quickly want something about as rich as the
|
||||
// pattern language in attenuators. And later, we could easily want
|
||||
// something with, perhaps, as much power as RELAX-NG or similar. (2)
|
||||
// Because we want to have onlookers have some hope of seeing whether
|
||||
// a pattern of interest to them is being observed, and if we used
|
||||
// attenuators to match, we'd have to expose visibility into
|
||||
// attenuators into the pattern language. See next question.
|
||||
//
|
||||
// Q. What kinds of constraints on the pattern language are there?
|
||||
//
|
||||
// A. It should be fast to evaluate; ideally, JITtable? It should
|
||||
// allow patterns on patterns, to some degree. This is for two
|
||||
// reasons: we occasionally want to observe observers, and we
|
||||
// frequently want to use attenuators limit the kinds of patterns that
|
||||
// a principal may observe. As such, it's good to choose a language
|
||||
// that enforced some kind of normal forms for its patterns, so
|
||||
// observer-observers and attenuator patterns don't have to deal with
|
||||
// spurious variation.
|
||||
//
|
||||
export const Observe = Record.makeConstructor('Observe', ['label', 'observer']);
|
||||
|
||||
export class Dataspace implements Partial<Entity> {
|
||||
readonly handleMap: IdentityMap<Handle, Record<Ref>> = new IdentityMap();
|
||||
readonly assertions = new Bag<Ref>();
|
||||
readonly subscriptions: Dictionary<Map<Ref, Dictionary<Handle>>> = new Dictionary();
|
||||
|
||||
assert(turn: Turn, rec: Assertion, handle: Handle): void {
|
||||
if (!Record.isRecord<Ref>(rec)) return;
|
||||
this.handleMap.set(handle, rec);
|
||||
if (this.assertions.change(rec, +1) !== ChangeDescription.ABSENT_TO_PRESENT) return;
|
||||
if (Observe.isClassOf(rec)) {
|
||||
const label = Observe._.label(rec)!;
|
||||
const observer = Observe._.observer(rec) as Ref;
|
||||
const seen = new Dictionary<Handle>();
|
||||
if (!this.subscriptions.has(label)) this.subscriptions.set(label, new Map());
|
||||
this.subscriptions.get(label)!.set(observer, seen);
|
||||
this.assertions.forEach((_count, prev) =>
|
||||
is((prev as Record<Ref>).label, label)
|
||||
&& seen.set(prev, turn.assert(observer, prev)));
|
||||
}
|
||||
this.subscriptions.get(rec.label)?.forEach((seen, peer) =>
|
||||
seen.has(rec) || seen.set(rec, turn.assert(peer, rec)));
|
||||
}
|
||||
|
||||
retract(turn: Turn, upstreamHandle: Handle): void {
|
||||
const rec = this.handleMap.get(upstreamHandle);
|
||||
if (rec === void 0) return;
|
||||
this.handleMap.delete(upstreamHandle);
|
||||
if (this.assertions.change(rec, -1) !== ChangeDescription.PRESENT_TO_ABSENT) return;
|
||||
this.subscriptions.get(rec.label)?.forEach((seen, _peer) => {
|
||||
const downstreamHandle = seen.get(rec);
|
||||
if (downstreamHandle !== void 0) {
|
||||
turn.retract(downstreamHandle);
|
||||
seen.delete(rec);
|
||||
}
|
||||
});
|
||||
if (Observe.isClassOf(rec)) {
|
||||
let peerMap = this.subscriptions.get(Observe._.label(rec)!)!;
|
||||
peerMap.delete(Observe._.observer(rec) as Ref);
|
||||
if (peerMap.size === 0) this.subscriptions.delete(Observe._.label(rec)!);
|
||||
}
|
||||
}
|
||||
|
||||
message(turn: Turn, rec: Assertion): void {
|
||||
if (!Record.isRecord<Ref>(rec)) return;
|
||||
this.subscriptions.get(rec.label)?.forEach((_seen, peer) => turn.message(peer, rec));
|
||||
}
|
||||
}
|
79
main.ts
79
main.ts
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
Actor,
|
||||
Assertion,
|
||||
Entity,
|
||||
Handle,
|
||||
Ref,
|
||||
Turn,
|
||||
|
@ -9,82 +8,8 @@ import {
|
|||
pdiscard,
|
||||
rfilter,
|
||||
} from './actor.js';
|
||||
import { Dictionary, IdentityMap, is, Record } from 'preserves';
|
||||
import { Bag, ChangeDescription } from './bag';
|
||||
|
||||
// Q. Why keep "Observe"? Why not do the clever trick of asserting the
|
||||
// observer, and having the dataspace read the implicit pattern it's
|
||||
// interested in off its attenuator?
|
||||
//
|
||||
// A. (1) Because we want to have the possibility of more than one
|
||||
// variety of pattern language. For example, here we have a simple
|
||||
// label match, but we'll quickly want something about as rich as the
|
||||
// pattern language in attenuators. And later, we could easily want
|
||||
// something with, perhaps, as much power as RELAX-NG or similar. (2)
|
||||
// Because we want to have onlookers have some hope of seeing whether
|
||||
// a pattern of interest to them is being observed, and if we used
|
||||
// attenuators to match, we'd have to expose visibility into
|
||||
// attenuators into the pattern language. See next question.
|
||||
//
|
||||
// Q. What kinds of constraints on the pattern language are there?
|
||||
//
|
||||
// A. (1) It should be fast to evaluate; ideally, JITtable? (2) It
|
||||
// should allow patterns on patterns, to some degree. We occasionally
|
||||
// want to observe observers. As such, it'd be good to choose a
|
||||
// language that enforced some kind of normal forms for its patterns,
|
||||
// so observer-observers didn't have to deal with spurious variation.
|
||||
//
|
||||
const Observe = Record.makeConstructor('Observe', ['label', 'observer']);
|
||||
|
||||
class Dataspace implements Partial<Entity> {
|
||||
readonly handleMap: IdentityMap<Handle, Record<Ref>> = new IdentityMap();
|
||||
readonly assertions = new Bag<Ref>();
|
||||
readonly subscriptions: Dictionary<Map<Ref, Dictionary<Handle>>> = new Dictionary();
|
||||
|
||||
assert(turn: Turn, rec: Assertion, handle: Handle): void {
|
||||
if (!Record.isRecord<Ref>(rec)) return;
|
||||
this.handleMap.set(handle, rec);
|
||||
if (this.assertions.change(rec, +1) !== ChangeDescription.ABSENT_TO_PRESENT) return;
|
||||
if (Observe.isClassOf(rec)) {
|
||||
const label = Observe._.label(rec)!;
|
||||
const observer = Observe._.observer(rec) as Ref;
|
||||
const seen = new Dictionary<Handle>();
|
||||
if (!this.subscriptions.has(label)) this.subscriptions.set(label, new Map());
|
||||
this.subscriptions.get(label)!.set(observer, seen);
|
||||
this.assertions.forEach((_count, prev) =>
|
||||
is((prev as Record<Ref>).label, label)
|
||||
&& seen.set(prev, turn.assert(observer, prev)));
|
||||
}
|
||||
this.subscriptions.get(rec.label)?.forEach((seen, peer) =>
|
||||
seen.has(rec) || seen.set(rec, turn.assert(peer, rec)));
|
||||
}
|
||||
|
||||
retract(turn: Turn, upstreamHandle: Handle): void {
|
||||
const rec = this.handleMap.get(upstreamHandle);
|
||||
if (rec === void 0) return;
|
||||
this.handleMap.delete(upstreamHandle);
|
||||
if (this.assertions.change(rec, -1) !== ChangeDescription.PRESENT_TO_ABSENT) return;
|
||||
this.subscriptions.get(rec.label)?.forEach((seen, _peer) => {
|
||||
const downstreamHandle = seen.get(rec);
|
||||
if (downstreamHandle !== void 0) {
|
||||
turn.retract(downstreamHandle);
|
||||
seen.delete(rec);
|
||||
}
|
||||
});
|
||||
if (Observe.isClassOf(rec)) {
|
||||
let peerMap = this.subscriptions.get(Observe._.label(rec)!)!;
|
||||
peerMap.delete(Observe._.observer(rec) as Ref);
|
||||
if (peerMap.size === 0) this.subscriptions.delete(Observe._.label(rec)!);
|
||||
}
|
||||
}
|
||||
|
||||
message(turn: Turn, rec: Assertion): void {
|
||||
if (!Record.isRecord<Ref>(rec)) return;
|
||||
this.subscriptions.get(rec.label)?.forEach((_seen, peer) => turn.message(peer, rec));
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
import { Record } from 'preserves';
|
||||
import { Dataspace, Observe } from './dataspace.js';
|
||||
|
||||
const BoxState = Record.makeConstructor('BoxState', ['value']);
|
||||
const SetBox = Record.makeConstructor('SetBox', ['newValue']);
|
||||
|
|
Loading…
Reference in New Issue