Better record typing

This commit is contained in:
Tony Garnock-Jones 2021-02-25 23:23:51 +01:00
parent 080c5bd278
commit c7f5f487e7
2 changed files with 31 additions and 30 deletions

View File

@ -33,65 +33,66 @@ export type Rewrite = { pattern: Pattern, template: Template };
export const _CRec = Symbol.for('rec');
export const CRec = Record.makeConstructor<{label: Assertion, arity: number}, Ref>()(
_CRec, ['label', 'arity']);
export type CRec = ReturnType<typeof CRec>;
export const _CArr = Symbol.for('arr');
export const CArr = Record.makeConstructor<{arity: number}, Ref>()(
_CArr, ['arity']);
export type CArr = ReturnType<typeof CArr>;
export const _CDict = Symbol.for('dict');
export const CDict = Record.makeConstructor<{}, Ref>()(
_CDict, []);
export type CDict = ReturnType<typeof CDict>;
export type ConstructorSpec = CRec | CArr | CDict;
export type ConstructorSpec =
| Record<typeof _CRec, [string, number], Ref>
| Record<typeof _CArr, [number], Ref>
| Record<typeof _CDict, [], Ref>;
export const _PDiscard = Symbol.for('_');
export const PDiscard = Record.makeConstructor<{}, Ref>()(
_PDiscard, []);
export type PDiscard = ReturnType<typeof PDiscard>;
export const _PBind = Symbol.for('bind');
export const PBind = Record.makeConstructor<{name: string, pattern: Assertion}, Ref>()(
export const PBind = Record.makeConstructor<{name: string, pattern: Pattern}, Ref>()(
_PBind, ['name', 'pattern']);
export type PBind = ReturnType<typeof PBind>;
export const _PAnd = Symbol.for('and');
export const PAnd = Record.makeConstructor<{patterns: Array<Assertion>}, Ref>()(
export const PAnd = Record.makeConstructor<{patterns: Array<Pattern>}, Ref>()(
_PAnd, ['patterns']);
export type PAnd = ReturnType<typeof PAnd>;
export const _PNot = Symbol.for('not');
export const PNot = Record.makeConstructor<{pattern: Assertion}, Ref>()(
export const PNot = Record.makeConstructor<{pattern: Pattern}, Ref>()(
_PNot, ['pattern']);
export type PNot = ReturnType<typeof PNot>;
export const _Lit = Symbol.for('lit');
export const Lit = Record.makeConstructor<{value: Assertion}, Ref>()(
_Lit, ['value']);
export type Lit = ReturnType<typeof Lit>;
export const _PCompound = Symbol.for('compound');
export const PCompound =
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Assertion>}, Ref>()(
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Pattern>}, Ref>()(
_PCompound, ['ctor', 'members']);
export type PCompound = ReturnType<typeof PCompound>;
export type Pattern = PDiscard | PBind | PAnd | PNot | Lit | PCompound;
export type Pattern =
| Record<typeof _PDiscard, [], Ref>
| Record<typeof _PBind, [string, Pattern], Ref>
| Record<typeof _PAnd, [Pattern[]], Ref>
| Record<typeof _PNot, [Pattern], Ref>
| Record<typeof _Lit, [Assertion], Ref>
| Record<typeof _PCompound, [ConstructorSpec, Dictionary<Pattern>], Ref>;
export const _TRef = Symbol.for('ref');
export const TRef = Record.makeConstructor<{name: string}, Ref>()(
_TRef, ['name']);
export type TRef = ReturnType<typeof TRef>;
export const _TCompound = Symbol.for('compound');
export const TCompound =
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Assertion>}, Ref>()(
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Template>}, Ref>()(
_TCompound, ['ctor', 'members']);
export type TCompound = ReturnType<typeof TCompound>;
export type Template = TRef | Lit | TCompound;
export type Template =
| Record<typeof _TRef, [string], Ref>
| Record<typeof _Lit, [Assertion], Ref>
| Record<typeof _TCompound, [ConstructorSpec, Dictionary<Template>], Ref>;
export type Bindings = { [name: string]: Assertion };
@ -224,20 +225,20 @@ export function match(p: Pattern, v: Assertion): Bindings | null {
case _PDiscard:
return true;
case _PBind:
if (walk(PBind._.pattern(p) as Pattern, v)) {
if (walk(PBind._.pattern(p), v)) {
bindings[PBind._.name(p)] = v;
return true;
}
return false;
case _PAnd:
for (const pp of PAnd._.patterns(p) as Pattern[]) {
for (const pp of PAnd._.patterns(p)) {
if (!walk(pp, v)) return false;
}
return true;
case _PNot: {
const savedBindings = bindings;
bindings = {};
const result = !walk(PNot._.pattern(p) as Pattern, v)
const result = !walk(PNot._.pattern(p), v)
bindings = savedBindings;
return result;
}
@ -245,10 +246,10 @@ export function match(p: Pattern, v: Assertion): Bindings | null {
return is(Lit._.value(p), v);
case _PCompound: {
const ctor = PCompound._.ctor(p);
const members = PCompound._.members(p) as Dictionary<Pattern, Ref>;
const members = PCompound._.members(p);
switch (ctor.label) {
case _CRec:
if (!Record.isRecord<Assertion, Ref>(v)) return false;
if (!Record.isRecord<Assertion, any, Ref>(v)) return false;
if (!is(CRec._.label(ctor), v.label)) return false;
if (CRec._.arity(ctor) !== v.length) return false;
for (const [key, pp] of members) {
@ -293,10 +294,10 @@ export function instantiate(t: Template, b: Bindings): Assertion {
return Lit._.value(t);
case _TCompound: {
const ctor = TCompound._.ctor(t);
const members = TCompound._.members(t) as Dictionary<Template, Ref>;
const members = TCompound._.members(t);
switch (ctor.label) {
case _CRec: {
const v = Record<Assertion, Ref>(CRec._.label(ctor), []);
const v = Record<Assertion, any, Ref>(CRec._.label(ctor), []);
v.length = CRec._.arity(ctor);
for (const [key, tt] of members) {
v[key as number] = walk(tt);

View File

@ -31,12 +31,12 @@ export const Observe = Record.makeConstructor<{label: Assertion, observer: Ref},
Symbol.for('Observe'), ['label', 'observer']);
export class Dataspace implements Partial<Entity> {
readonly handleMap: IdentityMap<Handle, Record<Assertion, Ref>> = new IdentityMap();
readonly handleMap: IdentityMap<Handle, Record<Assertion, any, 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<Assertion, Ref>(rec)) return;
if (!Record.isRecord<Assertion, any, Ref>(rec)) return;
this.handleMap.set(handle, rec);
if (this.assertions.change(rec, +1) !== ChangeDescription.ABSENT_TO_PRESENT) return;
if (Observe.isClassOf(rec)) {
@ -46,7 +46,7 @@ export class Dataspace implements Partial<Entity> {
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<Assertion, Ref>).label, label)
is((prev as Record<Assertion, any, Ref>).label, label)
&& seen.set(prev, turn.assert(observer, prev)));
}
this.subscriptions.get(rec.label)?.forEach((seen, peer) =>
@ -73,7 +73,7 @@ export class Dataspace implements Partial<Entity> {
}
message(turn: Turn, rec: Assertion): void {
if (!Record.isRecord<Assertion, Ref>(rec)) return;
if (!Record.isRecord<Assertion, any, Ref>(rec)) return;
this.subscriptions.get(rec.label)?.forEach((_seen, peer) => turn.message(peer, rec));
}
}