It isn't really sensible to have a Ref for non-Entities

This commit is contained in:
Tony Garnock-Jones 2021-02-23 14:35:23 +01:00
parent ecef864793
commit cd7efd615e
2 changed files with 26 additions and 25 deletions

View File

@ -1,6 +1,6 @@
import { IdentitySet, Value } from 'preserves';
export type Assertion = Value<Ref<Entity>>;
export type Assertion = Value<Ref>;
export type Handle = number;
@ -16,21 +16,21 @@ export interface Entity {
[message]?(turn: Turn, message: Assertion): void;
}
export class Ref<T> {
export class Ref {
readonly actor: Actor;
readonly target: T;
readonly target: Entity;
constructor(actor: Actor, target: T) {
constructor(actor: Actor, target: Entity) {
this.actor = actor;
this.target = target;
}
sync(turn: Turn, syncable: Ref<LocalAction>) {
turn.enqueue(syncable.actor, t => syncable.target(t));
sync(turn: Turn, syncable: Ref) {
turn.enqueue(syncable.actor, t => t.message(syncable, true));
}
}
export type OutboundMap = Map<Handle, Ref<Entity>>;
export type OutboundMap = Map<Handle, Ref>;
export class Actor {
readonly outbound: OutboundMap;
@ -79,7 +79,7 @@ export class Turn {
this.actor = actor;
}
ref<T>(t: T): Ref<T> {
ref(t: Entity): Ref {
return new Ref(this.actor, t);
}
@ -99,7 +99,7 @@ export class Turn {
this.tasks.push(t => this.actor.terminateWith(t, { ok: true }));
}
assert(location: Ref<Entity>, assertion: Assertion): Handle {
assert(location: Ref, assertion: Assertion): Handle {
const h = nextHandle++;
this.enqueue(location.actor, t => {
this.actor.outbound.set(h, location);
@ -112,24 +112,25 @@ export class Turn {
this._retract(this.actor.outbound.get(h)!, h);
}
replace(location: Ref<Entity>, h: Handle | undefined, assertion: Assertion): Handle {
replace(location: Ref, h: Handle | undefined, assertion: Assertion): Handle {
const newHandle = this.assert(location, assertion);
if (h !== void 0) this.retract(h);
return newHandle;
}
_retract(location: Ref<Entity>, handle: Handle): void {
_retract(location: Ref, handle: Handle): void {
this.enqueue(location.actor, t => {
this.actor.outbound.delete(handle);
location.target[retract]?.(t, handle);
});
}
sync(loc: Ref<any>): Promise<Turn> {
return new Promise(resolve => this.enqueue(loc.actor, t => loc.sync(t, this.ref(resolve))));
sync(loc: Ref): Promise<Turn> {
return new Promise(resolve => this.enqueue(loc.actor, t =>
loc.sync(t, this.ref({ [message]: resolve }))));
}
message(location: Ref<Entity>, assertion: Assertion): void {
message(location: Ref, assertion: Assertion): void {
this.enqueue(location.actor, t => location.target[message]?.(t, assertion));
}

22
main.ts
View File

@ -2,25 +2,25 @@ import { Actor, Assertion, Entity, Handle, Ref, Turn, assert, message, retract }
import { Dictionary, IdentityMap, is, Record } from 'preserves';
import { Bag, ChangeDescription } from './bag';
const Observe = Record.makeConstructor<Ref<Entity>>('Observe', ['label', 'observer']);
const Observe = Record.makeConstructor<Ref>('Observe', ['label', 'observer']);
class Dataspace implements Entity {
readonly handleMap: IdentityMap<Handle, Record<Ref<Entity>>> = new IdentityMap();
readonly assertions = new Bag<Ref<Entity>>();
readonly subscriptions: Dictionary<Map<Ref<Entity>, Dictionary<Handle>>> = new Dictionary();
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<Entity>>(rec)) return;
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<Entity>;
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<Entity>>).label, label)
is((prev as Record<Ref>).label, label)
&& seen.set(prev, turn.assert(observer, prev)));
}
this.subscriptions.get(rec.label)?.forEach((seen, peer) =>
@ -41,19 +41,19 @@ class Dataspace implements Entity {
});
if (Observe.isClassOf(rec)) {
let peerMap = this.subscriptions.get(Observe._.label(rec)!)!;
peerMap.delete(Observe._.observer(rec) as Ref<Entity>);
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<Entity>>(rec)) return;
if (!Record.isRecord<Ref>(rec)) return;
this.subscriptions.get(rec.label)?.forEach((_seen, peer) => turn.message(peer, rec));
}
}
const BoxState = Record.makeConstructor<Ref<Entity>>('BoxState', ['value']);
const SetBox = Record.makeConstructor<Ref<Entity>>('SetBox', ['newValue']);
const BoxState = Record.makeConstructor<Ref>('BoxState', ['value']);
const SetBox = Record.makeConstructor<Ref>('SetBox', ['newValue']);
let startTime = Date.now();
let prevValue = 0;