gdp-wired 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/dist/core/dsl.d.ts +30 -25
- package/dist/core/dsl.js +78 -32
- package/dist/core/gdp.d.ts +2 -2
- package/dist/core/gdp.js +2 -1
- package/dist/core/machine.d.ts +10 -0
- package/dist/lean-kernel/emit.d.ts +1 -0
- package/dist/lean-kernel/emit.js +130 -84
- package/dist/lean-kernel/library.js +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -209,8 +209,11 @@ const soon = retry.law('the next try is at most 64 after the last request', ({ne
|
|
|
209
209
|
- **Records and variants**: `record({limit: nat, blocked: bool})` hands out an object of its fields' values;
|
|
210
210
|
`variant({card: {...}, cash: {...}})` a value with `is(case)` and `match({card: c => ..., cash: c => ...})`, the only
|
|
211
211
|
place a case's fields are read. `Pay.make({tag: 'card', amount, verified})` builds a case to write or to pass to
|
|
212
|
-
`when`, and `same(a, b)` compares two values of one kind part by part
|
|
213
|
-
|
|
212
|
+
`when`, and `same(a, b)` compares two values of one kind part by part; `optional(c, value)` is a value present where c
|
|
213
|
+
holds (a result that may be missing). At runtime a record is an object of its fields and a variant `{tag: 'card',
|
|
214
|
+
...fields}` (or the key given, `variant({...}, {tag: 'kind'})`); a field or case the kind doesn't declare is refused.
|
|
215
|
+
What a kind keeps true of the values a step writes (a case in range, a flag 0 or 1) is a law of its own, `r.kindLaws()`,
|
|
216
|
+
which proveRule, proveSteps and proveLeadsTo prove beside the laws they are given. Both are their fields'
|
|
214
217
|
variables underneath (`pay.card.amount`, and the case's index as `pay`), so the kernel, both runtimes and the Lean
|
|
215
218
|
twin see nothing new. Maps and lists of them: `.maps({Acct: record({...}).from({...})})` (`Acct.at(k)`,
|
|
216
219
|
`Acct.set(k, value)`, `Acct.each(k => value)`) and `.items({Queue: Job})` (`push`, `pop`, `set`, `insertAt`,
|
|
@@ -237,15 +240,17 @@ const soon = retry.law('the next try is at most 64 after the last request', ({ne
|
|
|
237
240
|
own), and the Lean export takes them as the hypothesis they are, proving them where the function has a Lean
|
|
238
241
|
definition. A function defined and proven in Lean is how a rule reaches past what the kernel decides: `examples/functions.ts`.
|
|
239
242
|
- **Recurrences and their lemmas** (`recur(name, {args, fns, zero, next}, lemmas, between)`): f(0, a..) = zero and
|
|
240
|
-
f(x + 1, a..) = next(x, f(x, a..), a..), the further arguments `args` (up to
|
|
241
|
-
the kernel's own or a read of a function in `fns` (`modBy(7)`, another recurrence). Each lemma is a law about every x
|
|
243
|
+
f(x + 1, a..) = next(x, f(x, a..), a..), the further arguments `args` (up to four) fixed along the recursion, each term
|
|
244
|
+
the kernel's own or a read of a function in `fns` (`modBy(7)`, another recurrence), the start's too. Each lemma is a law about every x
|
|
242
245
|
and f(x), proven when the function is made, all of them together by induction (the recurrence run as a machine, each
|
|
243
246
|
lemma a law of it, so one lemma may lean on another, and on the facts of the functions it reads); each law `between`
|
|
244
247
|
is about every x <= y and f(x), f(y) ("never shrinks"), proven by induction on y from x, with the lemmas. One that
|
|
245
248
|
isn't proven throws `Unproven` with the induction step it breaks at. The two equations, the lemmas and the laws
|
|
246
|
-
between are the function's facts. The runtimes compute f by the recurrence
|
|
247
|
-
|
|
248
|
-
|
|
249
|
+
between are the function's facts. The runtimes compute f by the recurrence, continuing from the last value computed at
|
|
250
|
+
the same further arguments (a rule that reads f at a growing count takes one step a request); the Lean export defines
|
|
251
|
+
`Recur.<name>` by it (a function of several arguments through an injective pairing, proven so in Lean) and checks the
|
|
252
|
+
kernel's own proof of every lemma and law between: the machine it was proven by, with its trees, and the run of it that
|
|
253
|
+
reaches x and f(x), so a lemma with a product (squares: f(x) = x·x) needs no tactic of Lean's (`examples/lemmas.ts`).
|
|
249
254
|
- **Liveness** (`rule.leadsTo(name, {from, to, rank, helpful})`, `proveLeadsTo`): whenever `from` holds, `to` holds later in
|
|
250
255
|
every run that takes each helpful request infinitely often; proven by a rank no step raises while waiting and each
|
|
251
256
|
helpful request lowers in its region. `rank` may return several terms (compared in order) and `helpful` may be a list
|
package/dist/core/dsl.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export declare function when<K extends string>(c: Cond, then: Choice<K>, otherwi
|
|
|
43
43
|
export declare function when(c: Cond, then: Str, otherwise: Str): Str;
|
|
44
44
|
export declare function when<V>(c: Cond, then: Maybe<V>, otherwise: Maybe<V>): Maybe<V>;
|
|
45
45
|
export declare function when(c: Cond, then: Cond, otherwise: Cond): Cond;
|
|
46
|
-
export declare function when<C extends Cases>(c: Cond, then: Variant<C>, otherwise: Variant<C>): Variant<C>;
|
|
46
|
+
export declare function when<C extends Cases, T extends string>(c: Cond, then: Variant<C, T>, otherwise: Variant<C, T>): Variant<C, T>;
|
|
47
47
|
export declare function when<T extends Record<string, unknown>>(c: Cond, then: T, otherwise: T): T;
|
|
48
48
|
export declare const choose: (c: Cond, then: Cond, otherwise: Cond) => Cond;
|
|
49
49
|
export declare function same(a: unknown, b: unknown): Cond;
|
|
@@ -65,6 +65,7 @@ export declare abstract class Kind<V> {
|
|
|
65
65
|
abstract assume(name: string): Cond[];
|
|
66
66
|
abstract build(name: string, get: (part: string) => Term): V;
|
|
67
67
|
value(name: string, at?: At): V;
|
|
68
|
+
invariant(_value: V): Cond[];
|
|
68
69
|
abstract encode(name: string, raw: Raw): Record<string, number>;
|
|
69
70
|
abstract random(name: string, rand: Rand, bound: number): Record<string, number>;
|
|
70
71
|
abstract store(name: string, value: unknown): Record<string, TermLike>;
|
|
@@ -152,6 +153,7 @@ export declare class Maybe<V> {
|
|
|
152
153
|
}): Term;
|
|
153
154
|
}
|
|
154
155
|
export declare const maybe: <V>(of: Kind<V>) => Kind<Maybe<V>>;
|
|
156
|
+
export declare const optional: <V>(c: Cond, value: V) => Maybe<V>;
|
|
155
157
|
type Fields = Record<string, Kind<unknown>>;
|
|
156
158
|
export declare const record: <const K extends Fields>(fields: K) => Kind<ValuesOf<K>>;
|
|
157
159
|
type Cases = Record<string, Fields>;
|
|
@@ -161,40 +163,41 @@ type CaseValues<C extends Cases> = {
|
|
|
161
163
|
type MatchCases<C extends Cases, T> = {
|
|
162
164
|
[k in keyof C]: (fields: ValuesOf<C[k]>) => T;
|
|
163
165
|
};
|
|
164
|
-
export declare class Variant<C extends Cases = Cases> {
|
|
166
|
+
export declare class Variant<C extends Cases = Cases, T extends string = 'tag'> {
|
|
165
167
|
readonly tag: Term;
|
|
166
168
|
readonly cases: CaseValues<C>;
|
|
167
|
-
readonly kind: VariantKind<C>;
|
|
168
|
-
constructor(tag: Term, cases: CaseValues<C>, kind: VariantKind<C>);
|
|
169
|
+
readonly kind: VariantKind<C, T>;
|
|
170
|
+
constructor(tag: Term, cases: CaseValues<C>, kind: VariantKind<C, T>);
|
|
169
171
|
is(k: keyof C & string): Cond;
|
|
170
172
|
match(cases: MatchCases<C, Cond>): Cond;
|
|
171
173
|
match(cases: MatchCases<C, TermLike>): Term;
|
|
172
174
|
match<T>(cases: MatchCases<C, T>): T;
|
|
173
175
|
}
|
|
174
|
-
export type VariantWrite<C extends Cases> = {
|
|
176
|
+
export type VariantWrite<C extends Cases, T extends string = 'tag'> = {
|
|
175
177
|
[k in keyof C]: {
|
|
176
|
-
[f in
|
|
178
|
+
[f in T | keyof C[k]]: f extends T ? k : C[k][f] extends Kind<infer V> ? Writes<V> : never;
|
|
177
179
|
};
|
|
178
180
|
}[keyof C];
|
|
179
|
-
export declare class VariantKind<C extends Cases> extends Kind<Variant<C>> {
|
|
181
|
+
export declare class VariantKind<C extends Cases, T extends string = 'tag'> extends Kind<Variant<C, T>> {
|
|
180
182
|
readonly cases: C;
|
|
183
|
+
readonly tagKey: T;
|
|
181
184
|
readonly names: (keyof C & string)[];
|
|
182
|
-
|
|
183
|
-
constructor(cases: C);
|
|
185
|
+
constructor(cases: C, tagKey: T);
|
|
184
186
|
index(k: string): number;
|
|
185
187
|
private payload;
|
|
186
188
|
private filled;
|
|
187
189
|
private at;
|
|
188
190
|
vars(name: string): string[];
|
|
189
191
|
assume(name: string): Cond[];
|
|
190
|
-
build(name: string, get: (part: string) => Term): Variant<C>;
|
|
191
|
-
|
|
192
|
+
build(name: string, get: (part: string) => Term): Variant<C, T>;
|
|
193
|
+
invariant(x: Variant<C, T>): Cond[];
|
|
194
|
+
make(write: VariantWrite<C, T>): Variant<C, T>;
|
|
192
195
|
store(name: string, value: unknown): Record<string, TermLike>;
|
|
193
196
|
decode(name: string, vars: Record<string, number>): {
|
|
194
|
-
|
|
197
|
+
[x: string]: (keyof C & string) | Raw;
|
|
195
198
|
};
|
|
196
199
|
empty(): {
|
|
197
|
-
|
|
200
|
+
[x: string]: (keyof C & string) | Raw;
|
|
198
201
|
};
|
|
199
202
|
encode(name: string, raw: Raw): {
|
|
200
203
|
[x: string]: number;
|
|
@@ -203,10 +206,13 @@ export declare class VariantKind<C extends Cases> extends Kind<Variant<C>> {
|
|
|
203
206
|
[x: string]: number;
|
|
204
207
|
};
|
|
205
208
|
sample(rand: Rand, bound: number): {
|
|
206
|
-
|
|
209
|
+
[x: string]: (keyof C & string) | Raw;
|
|
207
210
|
};
|
|
208
211
|
}
|
|
209
|
-
export declare
|
|
212
|
+
export declare function variant<const C extends Cases>(cases: C): VariantKind<C>;
|
|
213
|
+
export declare function variant<const C extends Cases, const T extends string>(cases: C, opts: {
|
|
214
|
+
tag: T;
|
|
215
|
+
}): VariantKind<C, T>;
|
|
210
216
|
export declare const intern: ((text: string) => number) & {
|
|
211
217
|
load(entries: [string, number][]): void;
|
|
212
218
|
};
|
|
@@ -326,7 +332,7 @@ export declare class ItemsRef<V> {
|
|
|
326
332
|
export type Forall = {
|
|
327
333
|
readonly forall: (key: Term, ...more: Term[]) => Cond;
|
|
328
334
|
};
|
|
329
|
-
export declare function forall(f: (key: Term, key2: Term, key3: Term, key4: Term) => Cond): Forall;
|
|
335
|
+
export declare function forall(f: (key: Term, key2: Term, key3: Term, key4: Term, key5: Term, key6: Term) => Cond): Forall;
|
|
330
336
|
export type Relation = {
|
|
331
337
|
readonly when: Cond;
|
|
332
338
|
readonly holds: Cond;
|
|
@@ -356,9 +362,7 @@ type RecurEnv<A extends string, U> = {
|
|
|
356
362
|
export type RecurDef<A extends string, U extends Record<string, FnDef>> = {
|
|
357
363
|
args?: readonly A[];
|
|
358
364
|
fns?: U;
|
|
359
|
-
zero: TermLike | ((env:
|
|
360
|
-
[a in A]: Term;
|
|
361
|
-
}) => TermLike);
|
|
365
|
+
zero: TermLike | ((env: RecurEnv<A, U>) => TermLike);
|
|
362
366
|
next: (x: Term, prev: Term, env: RecurEnv<A, U>) => TermLike;
|
|
363
367
|
};
|
|
364
368
|
export declare const recur: <const A extends string = never, U extends Record<string, FnDef> = {}>(name: string, def: RecurDef<A, U>, lemmas?: Record<string, (x: Term, fx: Term, env: RecurEnv<A, U>) => Cond>, between?: Record<string, (x: Term, fx: Term, y: Term, fy: Term, env: RecurEnv<A, U>) => Cond>) => FnDef & {
|
|
@@ -369,7 +373,7 @@ type ValuesOf<K extends Kinds> = {
|
|
|
369
373
|
[n in keyof K]: K[n] extends Kind<infer V> ? V : never;
|
|
370
374
|
};
|
|
371
375
|
type Obj = object;
|
|
372
|
-
type Writes<V> = V extends Decimal ? DecimalLike | string : V extends Int ? IntLike : V extends Cond ? Cond | boolean : V extends Choice<infer K> ? Choice<K> | K : V extends Maybe<infer W> ? Maybe<W> | Writes<W> | null : V extends Str ? Str | string : V extends Variant<infer C> ? Variant<C> | VariantWrite<C> : V extends Term ? TermLike : V extends Record<string, unknown> ? {
|
|
376
|
+
type Writes<V> = V extends Decimal ? DecimalLike | string : V extends Int ? IntLike : V extends Cond ? Cond | boolean : V extends Choice<infer K> ? Choice<K> | K : V extends Maybe<infer W> ? Maybe<W> | Writes<W> | null : V extends Str ? Str | string : V extends Variant<infer C, infer T> ? Variant<C, T> | VariantWrite<C, T> : V extends Term ? TermLike : V extends Record<string, unknown> ? {
|
|
373
377
|
[k in keyof V]: Writes<V[k]>;
|
|
374
378
|
} : TermLike;
|
|
375
379
|
type Update<S extends Obj, M extends Obj> = Partial<{
|
|
@@ -458,6 +462,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
458
462
|
prune(order: keyof R & string, after: (p: P) => TermLike): Rule<P, I, S, M, F, R, Su, Q>;
|
|
459
463
|
law(name: string, f: (env: P & S & M & Su) => Cond | Forall): Law;
|
|
460
464
|
private lawOf;
|
|
465
|
+
kindLaws(): Law[];
|
|
461
466
|
stepLaw(name: string, then: (before: P & I & S & M & F & Su & Q, after: S & M & Su) => Cond | Forall, when?: (before: P & I & S & M & F & Su & Q) => Cond): StepLaw;
|
|
462
467
|
stepsLaw(name: string, k: number, then: (first: P & I & S & M & F & Su & Q, last: S & M & Su, requests: (I & Q)[]) => Cond | Forall, when?: (before: P & I & S & M & F & Su & Q, j: number) => Cond | undefined): StepLaw;
|
|
463
468
|
leadsTo(name: string, spec: {
|
|
@@ -552,18 +557,18 @@ export type Decision = {
|
|
|
552
557
|
then: C;
|
|
553
558
|
goal: Goal;
|
|
554
559
|
};
|
|
555
|
-
type ResultOf<V> = 0 extends 1 & V ? Term : [V] extends [Cond] ? Cond : [V] extends [Decimal] ? Decimal : [V] extends [Int] ? Int : [V] extends [Choice<infer K>] ? Choice<K> : [V] extends [Str] ? Str : [V] extends [Variant<infer C>] ? Variant<C> : [V] extends [Term | number] ? Term : [V] extends [Record<string, unknown>] ? {
|
|
560
|
+
type ResultOf<V> = 0 extends 1 & V ? Term : [V] extends [Cond] ? Cond : [V] extends [Decimal] ? Decimal : [V] extends [Int] ? Int : [V] extends [Choice<infer K>] ? Choice<K> : [V] extends [Str] ? Str : [V] extends [Variant<infer C, infer T>] ? Variant<C, T> : [V] extends [Maybe<infer W>] ? Maybe<ResultOf<W>> : [V] extends [Term | number] ? Term : [V] extends [Record<string, unknown>] ? {
|
|
556
561
|
[f in keyof V]: ResultOf<V[f]>;
|
|
557
562
|
} : Term;
|
|
558
563
|
type Result<T> = {
|
|
559
564
|
[k in keyof T]: ResultOf<T[k]>;
|
|
560
565
|
};
|
|
561
|
-
type ValueOf<V> = 0 extends 1 & V ? number : [V] extends [Cond] ? boolean : [V] extends [Choice<infer K>] ? K : [V] extends [Str] ? string : [V] extends [Variant<infer C>] ? CaseRaw<C> : [V] extends [Term | number | Int | Decimal] ? number : [V] extends [Record<string, unknown>] ? {
|
|
566
|
+
type ValueOf<V> = 0 extends 1 & V ? number : [V] extends [Cond] ? boolean : [V] extends [Choice<infer K>] ? K : [V] extends [Str] ? string : [V] extends [Variant<infer C, infer T>] ? CaseRaw<C, T> : [V] extends [Maybe<infer W>] ? ValueOf<W> | null : [V] extends [Term | number | Int | Decimal] ? number : [V] extends [Record<string, unknown>] ? {
|
|
562
567
|
[f in keyof V]: ValueOf<V[f]>;
|
|
563
568
|
} : number;
|
|
564
|
-
type CaseRaw<C extends Cases> = {
|
|
569
|
+
type CaseRaw<C extends Cases, T extends string = 'tag'> = {
|
|
565
570
|
[k in keyof C]: {
|
|
566
|
-
|
|
571
|
+
[t in T]: k;
|
|
567
572
|
} & {
|
|
568
573
|
[f in keyof C[k]]: C[k][f] extends Kind<infer V> ? ValueOf<V> : never;
|
|
569
574
|
};
|
|
@@ -571,7 +576,7 @@ type CaseRaw<C extends Cases> = {
|
|
|
571
576
|
type Value<T> = {
|
|
572
577
|
[k in keyof T]: ValueOf<T[k]>;
|
|
573
578
|
};
|
|
574
|
-
type Field = Term | Cond | Int | Decimal | Choice<string> | Str | Variant<any> | {
|
|
579
|
+
type Field = Term | Cond | Int | Decimal | Choice<string> | Str | Variant<any, any> | Maybe<any> | {
|
|
575
580
|
[f: string]: Field | number;
|
|
576
581
|
};
|
|
577
582
|
export declare class Fun<P extends Obj = {}, I extends Obj = {}, O extends Obj = {}> {
|
package/dist/core/dsl.js
CHANGED
|
@@ -158,6 +158,10 @@ const named = (at, n) => (typeof at === 'string' ? at + n : at(n));
|
|
|
158
158
|
const primed = (n) => `${n}'`;
|
|
159
159
|
export class Kind {
|
|
160
160
|
value(name, at = '') { return this.build(name, part => v(named(at, part))); }
|
|
161
|
+
// What every value a step writes keeps true of its variables (a flag is 0 or 1, a choice or a case in range, a
|
|
162
|
+
// presence bit 0 or 1), unlike a bound such as upTo's, which a step may pass: the laws a rule's state and cells of the
|
|
163
|
+
// kind are proven to keep (Rule.kindLaws).
|
|
164
|
+
invariant(_value) { return []; }
|
|
161
165
|
// A runtime value of the kind (what encode takes), for comparing a rule with the code it describes (compare).
|
|
162
166
|
sample(_rand, _bound) { throw new Error('this kind draws no runtime values'); }
|
|
163
167
|
// A state variable of this kind starting at a runtime value, or at a value over the params: `.state({open: bool.from(true)})`.
|
|
@@ -321,6 +325,7 @@ class BoolKind extends Kind {
|
|
|
321
325
|
vars(name) { return [name]; }
|
|
322
326
|
assume(name) { return [v(name).atMost(1)]; }
|
|
323
327
|
build(name, get) { return new Bool(get(name)); }
|
|
328
|
+
invariant(b) { return [b.n.atMost(1)]; }
|
|
324
329
|
store(name, value) { return { [name]: typeof value === 'boolean' ? (value ? 1 : 0) : count(value) }; }
|
|
325
330
|
decode(name, vars) { return vars[name] === 1; }
|
|
326
331
|
empty() { return false; }
|
|
@@ -370,6 +375,7 @@ class OneOfKind extends Kind {
|
|
|
370
375
|
vars(name) { return [name]; }
|
|
371
376
|
assume(name) { return [v(name).lt(this.values.length)]; }
|
|
372
377
|
build(name, get) { return new Choice(get(name), this.values); }
|
|
378
|
+
invariant(c) { return [c.n.lt(this.values.length)]; }
|
|
373
379
|
store(name, value) { return { [name]: value instanceof Choice ? value.n : this.encode(name, value)[name] }; }
|
|
374
380
|
decode(name, vars) { return this.values[vars[name]]; }
|
|
375
381
|
empty() { return this.values[0]; }
|
|
@@ -414,6 +420,7 @@ class MaybeKind extends Kind {
|
|
|
414
420
|
vars(name) { return [this.has(name), ...this.of.vars(name)]; }
|
|
415
421
|
assume(name) { return [v(this.has(name)).atMost(1), ...this.of.assume(name)]; }
|
|
416
422
|
build(name, get) { return new Maybe(get(this.has(name)), this.of.build(name, get)); }
|
|
423
|
+
invariant(m) { return [m.present.atMost(1), ...this.of.invariant(m.inner)]; }
|
|
417
424
|
// null for none, a Maybe as it is, any other value as some.
|
|
418
425
|
store(name, value) {
|
|
419
426
|
if (value === null || value === undefined)
|
|
@@ -435,6 +442,8 @@ class MaybeKind extends Kind {
|
|
|
435
442
|
sample(rand, bound) { return rand(4) === 0 ? null : this.of.sample(rand, bound); }
|
|
436
443
|
}
|
|
437
444
|
export const maybe = (of) => new MaybeKind(of);
|
|
445
|
+
// A value present where c holds and missing elsewhere (a function's result that may be missing, say).
|
|
446
|
+
export const optional = (c, value) => new Maybe(count(c), value);
|
|
438
447
|
const fieldName = (name, f) => `${name}.${f}`;
|
|
439
448
|
const plainFields = (what, names) => { for (const f of names)
|
|
440
449
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(f))
|
|
@@ -473,6 +482,7 @@ class RecordKind extends Kind {
|
|
|
473
482
|
vars(name) { composite(name); return this.each((f, k) => k.vars(fieldName(name, f))).flat(); }
|
|
474
483
|
assume(name) { return this.each((f, k) => k.assume(fieldName(name, f))).flat(); }
|
|
475
484
|
build(name, get) { return Object.fromEntries(this.each((f, k) => [f, k.build(fieldName(name, f), get)])); }
|
|
485
|
+
invariant(r) { return this.each((f, k) => k.invariant(r[f])).flat(); }
|
|
476
486
|
store(name, value) {
|
|
477
487
|
const o = value;
|
|
478
488
|
if (o === null || typeof o !== 'object')
|
|
@@ -516,19 +526,20 @@ export class Variant {
|
|
|
516
526
|
}
|
|
517
527
|
export class VariantKind extends Kind {
|
|
518
528
|
cases;
|
|
529
|
+
tagKey;
|
|
519
530
|
names;
|
|
520
|
-
tagKey
|
|
521
|
-
constructor(cases) {
|
|
531
|
+
constructor(cases, tagKey) {
|
|
522
532
|
super();
|
|
523
533
|
this.cases = cases;
|
|
534
|
+
this.tagKey = tagKey;
|
|
524
535
|
this.names = Object.keys(cases);
|
|
525
536
|
if (this.names.length === 0)
|
|
526
537
|
throw new Error('a variant has a case');
|
|
527
538
|
plainFields('variant', this.names);
|
|
528
539
|
for (const k of this.names) {
|
|
529
540
|
plainFields(`variant case ${k}`, Object.keys(cases[k]));
|
|
530
|
-
if (
|
|
531
|
-
throw new Error(`variant case ${k}:
|
|
541
|
+
if (tagKey in cases[k])
|
|
542
|
+
throw new Error(`variant case ${k}: ${tagKey} names the case, not a field`);
|
|
532
543
|
}
|
|
533
544
|
}
|
|
534
545
|
index(k) {
|
|
@@ -547,6 +558,7 @@ export class VariantKind extends Kind {
|
|
|
547
558
|
}
|
|
548
559
|
// The value of a case with these fields (terms, values or runtime values), to compare, match or pass to `when`:
|
|
549
560
|
// when(held, At.make({tag: 'holding', amount, since: now}), at)
|
|
561
|
+
invariant(x) { return [x.tag.lt(this.names.length), ...this.names.filter(k => this.filled(k)).flatMap(k => this.payload(k).invariant(x.cases[k]))]; }
|
|
550
562
|
make(write) {
|
|
551
563
|
const parts = this.store('$', write);
|
|
552
564
|
return this.build('$', part => { const t = parts[part]; return typeof t === 'number' ? lit(t) : t; });
|
|
@@ -586,7 +598,12 @@ export class VariantKind extends Kind {
|
|
|
586
598
|
}
|
|
587
599
|
sample(rand, bound) { const k = this.names[rand(this.names.length)]; return { [this.tagKey]: k, ...(this.filled(k) ? this.payload(k).sample(rand, bound) : {}) }; }
|
|
588
600
|
}
|
|
589
|
-
|
|
601
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
602
|
+
export function variant(cases, opts = {}) {
|
|
603
|
+
if (opts.tag !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(opts.tag))
|
|
604
|
+
throw new Error(`variant: ${opts.tag} is not a plain key`);
|
|
605
|
+
return new VariantKind(cases, opts.tag ?? 'tag');
|
|
606
|
+
}
|
|
590
607
|
// A string, encoded as its interned name, its length and one bit per pattern the kind declares (`like`, Cedar's glob:
|
|
591
608
|
// `*` any run of characters, `\*` a star): all computed from the string itself when the input is encoded, so the bits
|
|
592
609
|
// are the string's, not an answer someone supplies. Names come from one table per process, the empty string 0; a law
|
|
@@ -1084,7 +1101,7 @@ export const modBy = (c) => {
|
|
|
1084
1101
|
[`r(x + ${c}) = r(x)`]: forall(x => f(x.plus(c)).eq(f(x))),
|
|
1085
1102
|
}) });
|
|
1086
1103
|
};
|
|
1087
|
-
const reservedInRecur = ['x', 'fx', 'y', 'fy', 'prev', 'x0'];
|
|
1104
|
+
const reservedInRecur = ['x', 'fx', 'y', 'fy', 'prev', 'x0', 'fx0'];
|
|
1088
1105
|
export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
1089
1106
|
const plain = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
1090
1107
|
if (!plain.test(name))
|
|
@@ -1094,15 +1111,13 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1094
1111
|
if (!plain.test(n) || reservedInRecur.includes(n) || n === name)
|
|
1095
1112
|
throw new Error(`recur ${name}: ${n} can't name an argument or a function here (${reservedInRecur.join(', ')} and ${name} are taken)`);
|
|
1096
1113
|
distinctNames(`recur ${name}`, [...args, ...uses.map(([u]) => u)]);
|
|
1097
|
-
if (args.length >
|
|
1098
|
-
throw new Error(`recur ${name}:
|
|
1099
|
-
if (args.length > 3)
|
|
1100
|
-
throw new Error(`recur ${name}: at most three further arguments`);
|
|
1114
|
+
if (args.length > 4)
|
|
1115
|
+
throw new Error(`recur ${name}: at most four further arguments`);
|
|
1101
1116
|
const named = (n) => new Term({ op: 'var', name: n });
|
|
1102
1117
|
const argsAt = (ts) => Object.fromEntries(args.map((a, i) => [a, ts[i]]));
|
|
1103
1118
|
const usesAt = (at) => Object.fromEntries(uses.map(([u, d]) => [u, fnTerm(at(u), d)]));
|
|
1104
1119
|
const envAt = (ts, at = u => u) => ({ ...argsAt(ts), ...usesAt(at) });
|
|
1105
|
-
const zeroAt = (ts) => (typeof def.zero === 'function' ? def.zero(
|
|
1120
|
+
const zeroAt = (ts, at = u => u) => (typeof def.zero === 'function' ? def.zero(envAt(ts, at)) : def.zero);
|
|
1106
1121
|
// The recurrence over named variables (x, prev, the arguments; lemmas over x and fx; laws between over x, fx, y, fy),
|
|
1107
1122
|
// the functions it reads by their own names: what the runtimes and the Lean export are made from.
|
|
1108
1123
|
const own = args.map(named);
|
|
@@ -1117,7 +1132,7 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1117
1132
|
if (!maps.includes(m))
|
|
1118
1133
|
throw new Error(`recur ${name}: ${what} reads a function or a map it isn't given (${m})`);
|
|
1119
1134
|
};
|
|
1120
|
-
only('zero', zero, args, []);
|
|
1135
|
+
only('zero', zero, args, uses.map(([u]) => u));
|
|
1121
1136
|
only('next', next, ['x', 'prev', ...args], uses.map(([u]) => u));
|
|
1122
1137
|
const stated = Object.entries(lemmas).map(([n, f]) => { const holds = f(named('x'), named('fx'), envAt(own)).c; only(`lemma "${n}"`, holds, ['x', 'fx', ...args], uses.map(([u]) => u)); return { name: n, holds }; });
|
|
1123
1138
|
const pairs = Object.entries(between).map(([n, f]) => { const holds = f(named('x'), named('fx'), named('y'), named('fy'), envAt(own)).c; only(`law between "${n}"`, holds, ['x', 'fx', 'y', 'fy', ...args], uses.map(([u]) => u)); return { name: n, holds }; });
|
|
@@ -1126,44 +1141,55 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1126
1141
|
throw new Unproven(`${name}'s ${what}`, v); };
|
|
1127
1142
|
// The lemmas: x counting up from 0 and fx its value, each lemma a law of that machine.
|
|
1128
1143
|
let verdict = { proven: true, checked: true };
|
|
1144
|
+
const proofs = {};
|
|
1129
1145
|
if (stated.length) {
|
|
1130
|
-
|
|
1146
|
+
// A start that reads a function is a param given as that start by a fact (a start reads the params alone).
|
|
1147
|
+
const zeroReads = JSON.stringify(zero).includes('"op":"get"');
|
|
1148
|
+
let r0 = rule(`${name}, by induction`).params({ ...argKinds, ...(zeroReads ? { fx0: nat } : {}) }).fns(def.fns ?? {}).inputs({});
|
|
1149
|
+
if (zeroReads)
|
|
1150
|
+
r0 = r0.facts((p) => ({ [`fx0 is ${name}(0)`]: p.fx0.eq(zeroAt(args.map(a => p[a]))) }));
|
|
1151
|
+
const r = r0.state({ x: 0, fx: ((p) => (zeroReads ? p.fx0 : zeroAt(args.map(a => p[a])))) })
|
|
1131
1152
|
.step((env) => ({ x: env.x.plus(1), fx: def.next(env.x, env.fx, envAt(args.map(a => env[a]))) }));
|
|
1132
|
-
|
|
1153
|
+
const laws = Object.entries(lemmas).map(([n, f]) => r.law(n, (env) => f(env.x, env.fx, envAt(args.map(a => env[a])))));
|
|
1154
|
+
verdict = proveRule(r, laws);
|
|
1133
1155
|
failed('lemmas', verdict);
|
|
1156
|
+
proofs.lemmas = { machine: r.machine, laws };
|
|
1134
1157
|
}
|
|
1135
1158
|
const js = recurJs(name, args, zero, next, uses);
|
|
1136
|
-
const recurrence = { args, zero, next, lemmas: stated, between: pairs, uses: Object.fromEntries(uses.map(([u, d]) => [u, { arity: d.arity, ...(d.lean ? { lean: d.lean } : {}) }])) };
|
|
1159
|
+
const recurrence = { args, zero, next, lemmas: stated, between: pairs, uses: Object.fromEntries(uses.map(([u, d]) => [u, { arity: d.arity, ...(d.lean ? { lean: d.lean } : {}) }])), proofs };
|
|
1137
1160
|
// A law about every n keys (forall counts them by the callback's own parameters).
|
|
1138
1161
|
const keysOf = (n) => (f) => {
|
|
1139
1162
|
if (n === 0)
|
|
1140
1163
|
return f();
|
|
1141
|
-
const by = [(a) => f(a), (a, b) => f(a, b), (a, b, c) => f(a, b, c), (a, b, c, d) => f(a, b, c, d)
|
|
1164
|
+
const by = [(a) => f(a), (a, b) => f(a, b), (a, b, c) => f(a, b, c), (a, b, c, d) => f(a, b, c, d),
|
|
1165
|
+
(a, b, c, d, e) => f(a, b, c, d, e), (a, b, c, d, e, g) => f(a, b, c, d, e, g)][n - 1];
|
|
1142
1166
|
return forall(by);
|
|
1143
1167
|
};
|
|
1144
1168
|
const facts = (withBetween) => (f, at = {}) => {
|
|
1145
1169
|
const env = (ts) => ({ ...argsAt(ts), ...Object.fromEntries(uses.map(([u, d]) => [u, at[u] ?? fnTerm(u, d)])) });
|
|
1146
1170
|
return {
|
|
1147
|
-
[`${name}(0)`]: keysOf(args.length)((...as) => f(0, ...as).eq(
|
|
1171
|
+
[`${name}(0)`]: keysOf(args.length)((...as) => f(0, ...as).eq(typeof def.zero === 'function' ? def.zero(env(as)) : def.zero)),
|
|
1148
1172
|
[`${name}(x + 1)`]: keysOf(1 + args.length)((k, ...as) => f(k.plus(1), ...as).eq(def.next(k, f(k, ...as), env(as)))),
|
|
1149
1173
|
...Object.fromEntries(Object.entries(lemmas).map(([n, l]) => [n, keysOf(1 + args.length)((k, ...as) => l(k, f(k, ...as), env(as)))])),
|
|
1150
1174
|
...(withBetween ? Object.fromEntries(Object.entries(between).map(([n, l]) => [n, keysOf(2 + args.length)((k, j, ...as) => k.atMost(j).implies(l(k, f(k, ...as), j, f(j, ...as), env(as))))])) : {}),
|
|
1151
1175
|
};
|
|
1152
1176
|
};
|
|
1153
1177
|
const base = { arity: 1 + args.length, js, lean: `Recur.${name}`, recur: recurrence, ...(uses.length ? { uses: def.fns } : {}) };
|
|
1154
|
-
// The laws between two points: y counting up from x
|
|
1155
|
-
// of
|
|
1178
|
+
// The laws between two points: y counting up from x and fy from fx, a value at x that meets the lemmas (as f(x)
|
|
1179
|
+
// does: they are given of it, as facts of the params), each law a law of that machine beside the lemmas at y. What
|
|
1180
|
+
// holds there for every such fx holds for f(x), and fy is f(y) by the recurrence; the machine reads no f at all.
|
|
1156
1181
|
if (pairs.length) {
|
|
1157
|
-
const self = { ...base, facts: facts(false) };
|
|
1158
|
-
const r = rule(`${name}, between`).params({ ...argKinds, x: nat, fx: nat }).fns({ [name]: self }).inputs({})
|
|
1159
|
-
.facts(((p) => ({ [`fx is ${name}(x)`]: p.fx.eq(p[name](p.x, ...args.map(a => p[a]))) })))
|
|
1160
|
-
.state({ y: ((p) => p.x), fy: ((p) => p.fx) })
|
|
1161
|
-
.step((env) => ({ y: env.y.plus(1), fy: def.next(env.y, env.fy, envAt(args.map(a => env[a]), u => `${name}.${u}`)) }));
|
|
1162
1182
|
const at = (env) => args.map(a => env[a]);
|
|
1163
|
-
|
|
1164
|
-
|
|
1183
|
+
let r0 = rule(`${name}, between`).params({ ...argKinds, x: nat, fx: nat }).fns(def.fns ?? {}).inputs({});
|
|
1184
|
+
if (stated.length)
|
|
1185
|
+
r0 = r0.facts((p) => Object.fromEntries(Object.entries(lemmas).map(([n, l]) => [`${n} at x`, l(p.x, p.fx, envAt(at(p)))])));
|
|
1186
|
+
const r = r0.state({ y: ((p) => p.x), fy: ((p) => p.fx) })
|
|
1187
|
+
.step((env) => ({ y: env.y.plus(1), fy: def.next(env.y, env.fy, envAt(at(env))) }));
|
|
1188
|
+
const laws = [...Object.entries(lemmas).map(([n, l]) => r.law(`${n} at y`, (env) => l(env.y, env.fy, envAt(at(env))))),
|
|
1189
|
+
...Object.entries(between).map(([n, l]) => r.law(n, (env) => l(env.x, env.fx, env.y, env.fy, envAt(at(env)))))];
|
|
1165
1190
|
const v = proveRule(r, laws);
|
|
1166
1191
|
failed('laws between two points', v);
|
|
1192
|
+
proofs.between = { machine: r.machine, laws };
|
|
1167
1193
|
verdict = { proven: true, checked: verdict.checked && v.checked };
|
|
1168
1194
|
}
|
|
1169
1195
|
return { ...base, verdict, facts: facts(true) };
|
|
@@ -1201,7 +1227,9 @@ const recurJs = (name, args, zero, next, uses) => {
|
|
|
1201
1227
|
}
|
|
1202
1228
|
};
|
|
1203
1229
|
const inlined = uses.map(([u, d], i) => `const $f${i} = (${fnSource(u, d)}); `).join('');
|
|
1204
|
-
|
|
1230
|
+
// The last value computed at each tuple of further arguments is kept, so reading f at x after f at a smaller x takes
|
|
1231
|
+
// only the steps between (a rule that reads f at a count that grows costs one step a request).
|
|
1232
|
+
return `(() => { ${inlined}const $memo = new Map(); return (x, ${args.map(a => `${a}, `).join('')}) => { const $k = ${args.length ? `[${args.join(', ')}].join()` : "''"}; let $at = $memo.get($k); if (!$at || $at[0] > x) $at = [0, ${js(zero)}]; let x0 = $at[0], prev = $at[1]; for (; x0 < x; x0++) { prev = ((x, prev) => ${js(next)})(x0, prev); if (!Number.isSafeInteger(prev)) throw new RangeError(${JSON.stringify(`${name} passes the safe integers`)}); } if ($memo.size > 4096) $memo.clear(); $memo.set($k, [x, prev]); return prev; }; })()`;
|
|
1205
1233
|
};
|
|
1206
1234
|
// Functions with the functions they read, those first (named <function>.<use>), each with the terms its facts read them by.
|
|
1207
1235
|
const flatFns = (defs) => defs.flatMap(([n, d]) => {
|
|
@@ -1223,7 +1251,7 @@ const fnSource = (name, d) => {
|
|
|
1223
1251
|
}
|
|
1224
1252
|
if (d.arity === 1)
|
|
1225
1253
|
return src;
|
|
1226
|
-
return `k => { const u = z => { let s = Math.floor(Math.sqrt(z)); while (s * s > z) s--; while ((s + 1) * (s + 1) <= z) s++; return [s - (z - s * s), z - s * s]; }; const a = []; let z = k; for (let i = 1; i < ${d.arity}; i++) { const [p, q] = u(z); a.unshift(q); z = p; } a.unshift(z); return
|
|
1254
|
+
return `(() => { const $fn = (${src}); return k => { const u = z => { let s = Math.floor(Math.sqrt(z)); while (s * s > z) s--; while ((s + 1) * (s + 1) <= z) s++; return [s - (z - s * s), z - s * s]; }; const a = []; let z = k; for (let i = 1; i < ${d.arity}; i++) { const [p, q] = u(z); a.unshift(q); z = p; } a.unshift(z); return $fn(...a); }; })()`;
|
|
1227
1255
|
};
|
|
1228
1256
|
const fnTerm = (name, d) => (...args) => {
|
|
1229
1257
|
if (args.length !== d.arity)
|
|
@@ -1384,6 +1412,22 @@ export class Rule {
|
|
|
1384
1412
|
const [k1, more] = keysFor(out), holds = out.forall(v(k1), ...more.map(v)).c;
|
|
1385
1413
|
return more.length ? { name, forall: k1, keys: more, holds } : { name, forall: k1, holds: prenex(holds, `law ${name}`, k1) ?? holds };
|
|
1386
1414
|
}
|
|
1415
|
+
// What the kinds keep true of the state and of the maps and lists of a kind (Kind.invariant: a flag 0 or 1, a choice or
|
|
1416
|
+
// a case in range, a presence bit 0 or 1), as laws: proveRule, proveSteps and proveLeadsTo prove them beside the laws
|
|
1417
|
+
// they are given, so a law may lean on "the hold is idle, holding or released" without stating it; give them to
|
|
1418
|
+
// toLean (or the kernel) with the rest.
|
|
1419
|
+
kindLaws() {
|
|
1420
|
+
const out = [];
|
|
1421
|
+
const of = (env, n) => env[n];
|
|
1422
|
+
for (const [n, k] of this.spec.kinded ?? [])
|
|
1423
|
+
if (k.invariant(k.value(n)).length)
|
|
1424
|
+
out.push(this.law(`${n} is a value of its kind`, env => all(...k.invariant(of(env, n)))));
|
|
1425
|
+
for (const [n, k] of [...this.spec.cells ?? [], ...this.spec.items ?? []])
|
|
1426
|
+
if (k.invariant(k.value(n)).length) {
|
|
1427
|
+
out.push(this.law(`every ${n} cell is a value of its kind`, env => forall(key => all(...k.invariant(of(env, n).at(key))))));
|
|
1428
|
+
}
|
|
1429
|
+
return out;
|
|
1430
|
+
}
|
|
1387
1431
|
// A law about one step, for every request the rule allows: `then(before, after)` relates what the step reads (params,
|
|
1388
1432
|
// the request, state, maps, flags, sums, queries) to the state, maps and sums after it. `when` narrows the requests.
|
|
1389
1433
|
// counter.stepLaw('never goes back', ({used}, after) => used.atMost(after.used))
|
|
@@ -1876,7 +1920,7 @@ const encode = (kinds, values) => Object.assign({}, ...kinds.map(([n, k]) => k.e
|
|
|
1876
1920
|
export const rule = (name) => new Rule({ name, params: [], inputs: [], state: [], maps: [], flags: [], pre: [], assume: [], sums: [], queries: [] });
|
|
1877
1921
|
// The kind a result field is kept in.
|
|
1878
1922
|
const kindOf = (t) => (t instanceof Cond ? bool : t instanceof Int ? int : t instanceof Decimal ? decimal(t.places) : t instanceof Choice ? oneOf(...t.values) : t instanceof Str ? str({ like: t.patterns })
|
|
1879
|
-
: t instanceof Variant ? t.kind : isRecord(t) ? record(Object.fromEntries(Object.entries(t).map(([f, x]) => [f, kindOf(x)]))) : nat);
|
|
1923
|
+
: t instanceof Variant ? t.kind : t instanceof Maybe ? maybe(kindOf(t.inner)) : isRecord(t) ? record(Object.fromEntries(Object.entries(t).map(([f, x]) => [f, kindOf(x)]))) : nat);
|
|
1880
1924
|
export class Fun {
|
|
1881
1925
|
r;
|
|
1882
1926
|
out;
|
|
@@ -2019,16 +2063,18 @@ const goalsVerdict = (goals) => {
|
|
|
2019
2063
|
}
|
|
2020
2064
|
return { proven: true, checked: goals.every(g => check(g, trees[g.name])), trees };
|
|
2021
2065
|
};
|
|
2066
|
+
// The kinds' laws first, those the caller didn't pass already (by name).
|
|
2067
|
+
const withKinds = (r, laws) => [...r.kindLaws().filter(k => !laws.some(l => l.name === k.name)), ...laws];
|
|
2022
2068
|
export function proveRule(r, laws) {
|
|
2023
|
-
const out = proveLaws(r.machine, laws);
|
|
2069
|
+
const out = proveLaws(r.machine, withKinds(r, laws));
|
|
2024
2070
|
return { proven: out.proven, checked: out.checked, ...(out.failed ? { goal: out.failed.goal, stuck: out.failed.stuck, ...(out.failed.witness ? { witness: out.failed.witness } : {}), ...(out.failed.why ? { why: out.failed.why } : {}) } : {}) };
|
|
2025
2071
|
}
|
|
2026
2072
|
export function proveSteps(r, laws, stepLaws) {
|
|
2027
|
-
const out = proveStepLaws(r.machine, laws, stepLaws);
|
|
2073
|
+
const out = proveStepLaws(r.machine, withKinds(r, laws), stepLaws);
|
|
2028
2074
|
return { proven: out.proven, checked: out.checked, ...(out.failed ? { goal: out.failed.goal, stuck: out.failed.stuck, ...(out.failed.witness ? { witness: out.failed.witness } : {}), ...(out.failed.why ? { why: out.failed.why } : {}) } : {}) };
|
|
2029
2075
|
}
|
|
2030
2076
|
export function proveLeadsTo(r, laws, l) {
|
|
2031
|
-
const out = proveLiveness(r.machine, laws, l);
|
|
2077
|
+
const out = proveLiveness(r.machine, withKinds(r, laws), l);
|
|
2032
2078
|
return { proven: out.proven, checked: out.checked, ...(out.failed ? { goal: out.failed.goal, stuck: out.failed.stuck, ...(out.failed.witness ? { witness: out.failed.witness } : {}), ...(out.failed.why ? { why: out.failed.why } : {}) } : {}) };
|
|
2033
2079
|
}
|
|
2034
2080
|
export const proveDecision = (d) => goalsVerdict([d.goal]);
|
package/dist/core/gdp.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ export type Mint<K extends string, A> = <S extends AnyNamed[]>(args: A, ...about
|
|
|
6
6
|
export { Unproven };
|
|
7
7
|
type AnyFun = Fun<any, any, any>;
|
|
8
8
|
type Conditions<O> = {
|
|
9
|
-
[k in keyof O]: O[k] extends boolean ? k : never;
|
|
10
|
-
}[keyof O
|
|
9
|
+
[k in keyof O & string]: O[k] extends boolean ? k : O[k] extends Record<string, unknown> ? `${k}.${Conditions<O[k]>}` : never;
|
|
10
|
+
}[keyof O & string];
|
|
11
11
|
type ArgsOf<F extends AnyFun> = Parameters<F['run']>[0];
|
|
12
12
|
type ParamsOf<F extends AnyFun> = NonNullable<Parameters<F['run']>[1]>;
|
|
13
13
|
export declare function guard<K extends string, F extends AnyFun>(prover: Prover<K>, fn: F, field: Conditions<ReturnType<F['run']>>, opts: {
|
package/dist/core/gdp.js
CHANGED
|
@@ -4,6 +4,7 @@ import { allowed, requestEnv } from "./watch.js";
|
|
|
4
4
|
export { Unproven };
|
|
5
5
|
const proven = (what, v) => { if (!(v.proven && v.checked))
|
|
6
6
|
throw new Unproven(what, v); };
|
|
7
|
+
const atPath = (o, path) => path.split('.').reduce((x, f) => (typeof x === 'object' && x !== null ? x[f] : undefined), o);
|
|
7
8
|
// A proof wherever `fn`'s result `field` holds. Every law in `laws` must have been stated about `fn` (fn.law or
|
|
8
9
|
// fn.relate) and is proven now; the arguments it is later called with must meet the function's kinds and `assume`.
|
|
9
10
|
// const access = fun('document access').inputs({...}).returns(env => ({permit: permit(env)}))
|
|
@@ -23,7 +24,7 @@ export function guard(prover, fn, field, opts) {
|
|
|
23
24
|
const input = r.encodeInput(args);
|
|
24
25
|
if (m.assume && !evalC(m.assume, { ...ps, ...input }, { scalars: {}, maps: {} }, m))
|
|
25
26
|
return null;
|
|
26
|
-
return fn.run(args, params)
|
|
27
|
+
return atPath(fn.run(args, params), field) === true ? prover.prove(...about) : null;
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
30
|
// A proof for each request whose step takes `flag`, the rule's laws (and step laws) proven now. One gate holds one
|
package/dist/core/machine.d.ts
CHANGED
|
@@ -88,6 +88,16 @@ export type Recurrence = {
|
|
|
88
88
|
arity: number;
|
|
89
89
|
lean?: string;
|
|
90
90
|
}>;
|
|
91
|
+
proofs?: {
|
|
92
|
+
lemmas?: {
|
|
93
|
+
machine: Machine;
|
|
94
|
+
laws: Law[];
|
|
95
|
+
};
|
|
96
|
+
between?: {
|
|
97
|
+
machine: Machine;
|
|
98
|
+
laws: Law[];
|
|
99
|
+
};
|
|
100
|
+
};
|
|
91
101
|
};
|
|
92
102
|
export type History = {
|
|
93
103
|
keep: C;
|