import { KeyedDictionary, preserves } from '@preserves/core'; import { AnyValue, Assertion, Entity, Handle, LocalAction, Ref, Turn } from 'runtime/actor'; import { fromObserve, Observe } from '../gen/dataspace'; import { DBind, DDiscard, DLit, Pattern, DCompound, CArr, CDict, CRec } from '../gen/dataspacePatterns'; export { asObserve, fromObserve, toObserve, Observe, $Observe } from '../gen/dataspace'; export * from '../gen/dataspacePatterns'; // 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. (3) // Because reflection on attenuators is a big, heavy hammer, and it's // better to be explicit about patterns! Also, some attenuations // happen behind a veil of secrecy - they're not all open for the // world to read about. Actors may proxy communications in arbitrary, // secret ways. // // 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 class Dataspace implements Partial { assert(turn: Turn, rec: Assertion, handle: Handle): void { console.log(preserves`ds ${turn.activeFacet.id} assert ${rec} ${handle}`); throw new Error("Full dataspaces not implemented"); } retract(turn: Turn, upstreamHandle: Handle): void { console.log(preserves`ds ${turn.activeFacet.id} retract ${upstreamHandle}`); throw new Error("Full dataspaces not implemented"); } message(turn: Turn, rec: Assertion): void { console.log(preserves`ds ${turn.activeFacet.id} message ${rec}`); throw new Error("Full dataspaces not implemented"); } } export function during(f: (t: Turn, a: Assertion) => Promise): Partial { const assertionMap = new Map(); return { assert(t: Turn, a: Assertion, h: Handle): void { f(t, a).then(g => { if (g === null) g = _t => {}; switch (assertionMap.get(h)) { case void 0: assertionMap.set(h, g); break; case 'dead': assertionMap.delete(h); t.freshen(g); break; default: console.error('during: Duplicate handle in assert: ' + h); break; } }); }, retract(t: Turn, h: Handle): void { const g = assertionMap.get(h); switch (g) { case void 0: assertionMap.set(h, 'dead'); break; case 'dead': console.error('during: Duplicate handle in retract: ' + h); break; default: assertionMap.delete(h); g(t); } }, }; } export function observe(t: Turn, ds: Ref, pattern: Pattern, e: Partial): Handle { return t.assert(ds, fromObserve(Observe({ pattern, observer: t.ref(e) }))); } export namespace P { export function discard(): Pattern { return Pattern.DDiscard(DDiscard()); } export function bind(name: string, pattern: Pattern = discard()): Pattern { return Pattern.DBind(DBind({name: Symbol.for(name), pattern})); } export function lit(value: AnyValue): Pattern { return Pattern.DLit(DLit(value)); } export function rec(label: AnyValue, ...fields: Pattern[]): Pattern { const members = new KeyedDictionary(); fields.forEach((f, i) => { if (f._variant !== 'DDiscard') members.set(i, f); }); return Pattern.DCompound(DCompound.rec({ ctor: CRec({ label, arity: fields.length }), members, })); } export function arr(...entries: Pattern[]): Pattern { const members = new KeyedDictionary(); entries.forEach((f, i) => { if (f._variant !== 'DDiscard') members.set(i, f); }); return Pattern.DCompound(DCompound.arr({ ctor: CArr(entries.length), members, })); } export function dict(...entries: [AnyValue, Pattern][]): Pattern { const members = new KeyedDictionary(); entries.forEach(([k, p]) => members.set(k, p)); return Pattern.DCompound(DCompound.dict({ ctor: CDict(), members, })); } }