gdp-wired 0.6.0 → 0.8.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 +102 -10
- package/dist/core/dsl.d.ts +159 -12
- package/dist/core/dsl.js +483 -26
- package/dist/core/gdp.d.ts +31 -0
- package/dist/core/gdp.js +58 -0
- package/dist/core/machine.d.ts +18 -0
- package/dist/kernel/prove.js +39 -2
- package/dist/lean-kernel/emit.d.ts +8 -1
- package/dist/lean-kernel/emit.js +206 -8
- package/dist/lean-kernel/lean-terms.d.ts +4 -0
- package/dist/lean-kernel/lean-terms.js +38 -0
- package/package.json +18 -4
package/README.md
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
<
|
|
1
|
+
<picture>
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/monotykamary/gdp-wired/main/static/banner-dark.svg">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/monotykamary/gdp-wired/main/static/banner-light.svg" alt="gdp-wired: an automatic prover for stateful rules" width="100%">
|
|
4
|
+
</picture>
|
|
2
5
|
|
|
3
6
|
# gdp-wired
|
|
4
7
|
|
|
@@ -27,8 +30,10 @@ proveRule(budget, [bounded]) // {proven: true, checked: true}
|
|
|
27
30
|
Names are typed: `({s, bb}) => …` or a law that reads the request `c` is a TypeScript error, not a runtime surprise.
|
|
28
31
|
Forget the cap (`s.plus(c)`) and the kernel refuses the proof; a search from the start turns it into a real run:
|
|
29
32
|
`{params: {b: 1, m: 0}, trace: [{c: 2}]}`. `bun examples/budget.ts` runs all of it, including the generated JavaScript,
|
|
30
|
-
and `examples/domains/` holds
|
|
31
|
-
protocols, commerce, supply chains, games, education
|
|
33
|
+
and `examples/domains/` holds 187 rules from 21 domains (finance, access control, public services, operations,
|
|
34
|
+
protocols, commerce, supply chains, games, education, healthcare, energy, networking, AI agents, cloud, ad tech,
|
|
35
|
+
governance, insurance, bookings, safety and IoT, mobility, media), each checked three ways: the kernel proves every law
|
|
36
|
+
and refutes every broken variant, random runs of both runtimes agree, and Lean checks the trees.
|
|
32
37
|
|
|
33
38
|
## Where it fits
|
|
34
39
|
|
|
@@ -56,6 +61,12 @@ All of these are proven automatically, and each proof is also a Lean theorem:
|
|
|
56
61
|
- **Refinement**: an implementation behaves like a simpler spec, step for step (`examples/composition.ts`).
|
|
57
62
|
- **Functions**: division, remainder, and your own functions, known by their facts; a function defined in Lean brings
|
|
58
63
|
facts proven there (`examples/functions.ts`).
|
|
64
|
+
- **Lemmas you state**: a function defined by a recurrence (`recur`), of one argument or several, reading other
|
|
65
|
+
functions, and lemmas about it proven by induction, at one point or between two, which every rule that reads it may
|
|
66
|
+
then use: a backoff delay is at most 64 at every attempt and never shrinks (`examples/lemmas.ts`).
|
|
67
|
+
- **Records and variants**: inputs, state, results, map cells and list items made of named fields, or one of several
|
|
68
|
+
cases each with fields of its own (a payment by card or in cash, a seat free, held or sold), every case covered
|
|
69
|
+
(`examples/datatypes.ts`).
|
|
59
70
|
- **Pure functions and two-run laws**: laws about what a function returns, and laws comparing two calls, like "the
|
|
60
71
|
secret never changes the answer". `examples/jev-fabric/` ports a Bend project's policy core and proves all 59 laws its
|
|
61
72
|
Bend proofs state.
|
|
@@ -66,7 +77,7 @@ synthesizer finds one and the kernel proves it.
|
|
|
66
77
|
**What it leaves to Lean or Bend.** Two things, and neither is a gap the next feature closes: proofs the prover would
|
|
67
78
|
have to invent (induction over a recursive structure whose proof needs a lemma nobody stated, and arithmetic with
|
|
68
79
|
products of unknowns beyond multiplying two known facts; integer arithmetic with induction or with products is
|
|
69
|
-
undecidable), and shipping an arbitrary algorithm as its proved source (gdp-wired ships the rule as JavaScript; Bend
|
|
80
|
+
undecidable; a lemma you state about a recurrence is proven, but a recursive datatype as a value is Lean's), and shipping an arbitrary algorithm as its proved source (gdp-wired ships the rule as JavaScript; Bend
|
|
70
81
|
compiles the proved source itself). The full list of what is and isn't in scope is in
|
|
71
82
|
[`docs/comparison.md`](docs/comparison.md#gdp-wireds-scope-in-detail).
|
|
72
83
|
|
|
@@ -101,13 +112,46 @@ The untrusted parts are held to the trusted ones by the test suite:
|
|
|
101
112
|
tests, and the few that survive are equivalent (`kernel/MUTANTS.md`).
|
|
102
113
|
- **Fuzz**: the kernel never accepts a refutation of something that has a model.
|
|
103
114
|
|
|
115
|
+
## With gdp-ts
|
|
116
|
+
|
|
117
|
+
[gdp-ts](https://github.com/rauchg/gdp-ts) makes every call site present a proof that a check ran on exactly these
|
|
118
|
+
values, and trusts the module that performs the check. `gdp-wired/gdp` proves that check: the trusted module hands its
|
|
119
|
+
private prover to a `guard` (a function's condition) or a `gate` (a rule's flag), which proves the laws when the module
|
|
120
|
+
loads, refuses to load if one fails, and mints a proof only where the proven condition holds. gdp-ts then holds every
|
|
121
|
+
call site to it: the policy is proven, every caller is held to it, and Lean checks the proof again.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
// proofs/can-set-protection.ts (trusted: the only module that can mint this proof)
|
|
125
|
+
import {defineProof, type Named, type Proof} from '@gdp-ts/core'
|
|
126
|
+
import {fun, no, oneOf} from 'gdp-wired'
|
|
127
|
+
import {guard} from 'gdp-wired/gdp'
|
|
128
|
+
|
|
129
|
+
const canSet = fun('can set password protection')
|
|
130
|
+
.inputs({role: oneOf('owner', 'member', 'viewer', 'none'), plan: oneOf('free', 'pro', 'enterprise')})
|
|
131
|
+
.returns(({role, plan}) => ({allow: role.is('owner').or(role.is('member')).and(plan.is('free').not())}))
|
|
132
|
+
const laws = [canSet.law('the free plan never has protection', ({plan}, out) => [plan.is('free'), out.allow], () => no)]
|
|
133
|
+
|
|
134
|
+
const CanSetProtection = defineProof('CanSetProtection') // not exported
|
|
135
|
+
export interface CanSetProtection<U, P> extends Proof<'CanSetProtection', [U, P]> {}
|
|
136
|
+
const mint = guard(CanSetProtection, canSet, 'allow', {laws}) // proven here, or the module doesn't load
|
|
137
|
+
|
|
138
|
+
export async function canSetProtection<U, P>(user: Named<U, UserId>, project: Named<P, ProjectId>): Promise<CanSetProtection<U, P> | null> {
|
|
139
|
+
return mint({role: await db.role(user.value, project.value), plan: await db.plan(project.value)}, user, project)
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
A `gate` does the same for a rule with state: each request takes the rule's step, and a proof is minted when the step
|
|
144
|
+
takes the flag (a one-time token redeemed, a sign-in while the account is unlocked, an email within the hourly cap).
|
|
145
|
+
Requests outside what was proven (a failed `assume`, a history read out of order) mint nothing and leave the state as it
|
|
146
|
+
was. `examples/gdp.ts` runs gdp-ts's own example this way; `@gdp-ts/core` is an optional peer dependency.
|
|
147
|
+
|
|
104
148
|
## Writing laws
|
|
105
149
|
|
|
106
150
|
The builders (`core/dsl.ts`) hand out typed values for every name you declare; everything compiles to the plain machine
|
|
107
151
|
the kernel checks, so nothing in them is trusted.
|
|
108
152
|
|
|
109
153
|
```ts
|
|
110
|
-
import {bool, count, forall, maybe, nat, oneOf, proveSteps, relation, rule, yes} from 'gdp-wired'
|
|
154
|
+
import {bool, count, forall, fun, maybe, min, nat, no, oneOf, proveSteps, record, recur, relation, rule, variant, when, yes} from 'gdp-wired'
|
|
111
155
|
|
|
112
156
|
// Maps and flags: a grant is decided once per step; each map is written once; a map law holds at every key.
|
|
113
157
|
const caps = rule('caps')
|
|
@@ -142,12 +186,36 @@ const window = rule('window limit')
|
|
|
142
186
|
const trial = rule('trial').inputs({plan: oneOf('free', 'pro'), spent: maybe(nat), allowance: maybe(nat)})
|
|
143
187
|
const failsClosed = trial.decide('a missing fact means no trial', ({spent}) => spent.isNone,
|
|
144
188
|
({plan, spent, allowance}) => plan.match({free: () => spent.match({none: () => yes, some: s => allowance.match({none: () => yes, some: a => s.atLeast(a)})}), pro: () => yes}))
|
|
189
|
+
|
|
190
|
+
// Records and variants: a payment is by card or in cash, each with fields of its own, read only in `match`.
|
|
191
|
+
const Pay = variant({card: {amount: nat, verified: bool}, cash: {amount: nat}})
|
|
192
|
+
const approve = fun('approve').inputs({pay: Pay, who: record({limit: nat, blocked: bool})})
|
|
193
|
+
.returns(({pay, who}) => ({ok: who.blocked.not().and(pay.match({card: c => c.verified.and(c.amount.atMost(who.limit)), cash: c => c.amount.atMost(200)}))}))
|
|
194
|
+
const unverified = approve.law('an unverified card is never approved', ({pay}, out) => [pay.match({card: c => c.verified.not(), cash: () => no}), out.ok], () => no)
|
|
195
|
+
|
|
196
|
+
// A lemma you state, proven by induction: the backoff delay is defined by its recurrence, and its lemmas are facts
|
|
197
|
+
// every rule that reads it may use (its two equations alone don't bound it at an unknown attempt).
|
|
198
|
+
const delay = recur('delay', {zero: 1, next: (_, d) => min(d.times(2), 64)}, {'at least 1': (_, d) => d.atLeast(1), 'at most 64': (_, d) => d.atMost(64)})
|
|
199
|
+
const retry = rule('retry').inputs({failed: bool, now: nat}).state({tries: 0, next: 0, last: 0}).fns({delay})
|
|
200
|
+
.assume(({now, last}) => now.atLeast(last))
|
|
201
|
+
.step(({failed, now, tries, next, delay}) => ({tries: when(failed, tries.plus(1), 0), next: when(failed.and(now.atLeast(next)), now.plus(delay(tries)), next), last: now}))
|
|
202
|
+
const soon = retry.law('the next try is at most 64 after the last request', ({next, last}) => next.atMost(last.plus(64)))
|
|
145
203
|
```
|
|
146
204
|
|
|
147
205
|
|
|
148
|
-
- **Kinds**: `nat`, `upTo(k)`, `bool`, `oneOf(...)`, `maybe(kind)`, `int`, `decimal(places)`, `str({like, maxLength})
|
|
149
|
-
their assumptions (a bit is 0 or 1, a choice is in range) go to the kernel by
|
|
150
|
-
`encodeInput`/`encodeParams` turn runtime values into the numbers the machine reads.
|
|
206
|
+
- **Kinds**: `nat`, `upTo(k)`, `bool`, `oneOf(...)`, `maybe(kind)`, `int`, `decimal(places)`, `str({like, maxLength})`,
|
|
207
|
+
`record({...})`, `variant({...})`; their assumptions (a bit is 0 or 1, a choice is in range) go to the kernel by
|
|
208
|
+
themselves, and `encodeInput`/`encodeParams` turn runtime values into the numbers the machine reads.
|
|
209
|
+
- **Records and variants**: `record({limit: nat, blocked: bool})` hands out an object of its fields' values;
|
|
210
|
+
`variant({card: {...}, cash: {...}})` a value with `is(case)` and `match({card: c => ..., cash: c => ...})`, the only
|
|
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. At runtime a record is an object of its fields
|
|
213
|
+
and a variant `{tag: 'card', ...fields}`; a field or case the kind doesn't declare is refused. Both are their fields'
|
|
214
|
+
variables underneath (`pay.card.amount`, and the case's index as `pay`), so the kernel, both runtimes and the Lean
|
|
215
|
+
twin see nothing new. Maps and lists of them: `.maps({Acct: record({...}).from({...})})` (`Acct.at(k)`,
|
|
216
|
+
`Acct.set(k, value)`, `Acct.each(k => value)`) and `.items({Queue: Job})` (`push`, `pop`, `set`, `insertAt`,
|
|
217
|
+
`removeAt`, `apply`, `has`, `within`, `increasing(by)`), one map per variable of the kind; `rule.decode` reads them back
|
|
218
|
+
as objects and arrays. Not as values: recursive types (a tree).
|
|
151
219
|
- **State of any kind**: `.state({open: bool.from(true), status: oneOf('new', 'done').from('new'), owner: str().from(''), note: maybe(nat).from(null)})`;
|
|
152
220
|
a step writes each kind's own values, `when(c, a, b)` chooses between any two values of a kind, and `rule.decode(state)`
|
|
153
221
|
reads them back as runtime values.
|
|
@@ -168,6 +236,16 @@ const failsClosed = trial.decide('a missing fact means no trial', ({spent}) => s
|
|
|
168
236
|
take arguments, `perMinute(t)`; the kernel knows each only by its facts (`.facts(...)`, and a library function's
|
|
169
237
|
own), and the Lean export takes them as the hypothesis they are, proving them where the function has a Lean
|
|
170
238
|
definition. A function defined and proven in Lean is how a rule reaches past what the kernel decides: `examples/functions.ts`.
|
|
239
|
+
- **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 three) fixed along the recursion, each term
|
|
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
|
|
242
|
+
and f(x), proven when the function is made, all of them together by induction (the recurrence run as a machine, each
|
|
243
|
+
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
|
+
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
|
+
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 (x steps); the Lean export defines
|
|
247
|
+
`Recur.<name>` by it (a function of several arguments through an injective pairing, proven so in Lean) and proves
|
|
248
|
+
every fact there by induction (`examples/lemmas.ts`).
|
|
171
249
|
- **Liveness** (`rule.leadsTo(name, {from, to, rank, helpful})`, `proveLeadsTo`): whenever `from` holds, `to` holds later in
|
|
172
250
|
every run that takes each helpful request infinitely often; proven by a rank no step raises while waiting and each
|
|
173
251
|
helpful request lowers in its region. `rank` may return several terms (compared in order) and `helpful` may be a list
|
|
@@ -230,7 +308,7 @@ Lean, compiled natively, wins where the state stays small. Details in [`docs/com
|
|
|
230
308
|
| `core/` | Machines, the interpreter and JS compiler, the builders, composition (`compose.ts`), the linear solver and its checker, Houdini invariants, models, the first Lean export |
|
|
231
309
|
| `kernel/` | The trusted kernel and the untrusted prover, with soundness fuzz (`kernel.test.ts`, `history.test.ts`) |
|
|
232
310
|
| `lean-kernel/` | The kernel's Lean twin, its soundness proofs, and the exported trees |
|
|
233
|
-
| `examples/` | The builders at work: `domains/` (
|
|
311
|
+
| `examples/` | The builders at work: `domains/` (187 rules across 21 domains, each with laws and broken variants), `harness.ts` (the triple check), `readme.ts`, `budget.ts`, `gdp.ts` (gdp-ts's example, its checks proven), `datatypes.ts` (records and variants), `lemmas.ts` (recurrences and their lemmas), `composition.ts`, `sets.ts`, `quantifiers.ts`, `functions.ts`, `lists.ts`, `sizes.ts`, `liveness.ts`, `steps.ts` (laws over up to 16 steps), and `jev-fabric/` (a Bend project's 59 proven laws, ported) |
|
|
234
312
|
| `docs/` | The comparison with Bend and Lean, and the full scope |
|
|
235
313
|
| `compare/` | The same three rules in gdp-wired, Bend and plain Lean, checked and measured |
|
|
236
314
|
| `research/` | Everything around the library ([`research/README.md`](research/README.md)): the GPU searches (`search/`, `hw/`, `modal-gpu/`), invariant and rank synthesis (`synth/`), relational laws and guard synthesis (`next/`), bit-vector laws (`sat/`), the enumeration and benchmarks (`enum/`, `gpu/`, `perf/`), the first Lean checker (`lean/`) and the first prototype, the audit path (`audit/`) |
|
|
@@ -266,10 +344,24 @@ modal run modal/run.py --jobs modal/suite.json --out runs/modal/suite && sh moda
|
|
|
266
344
|
|
|
267
345
|
Each directory's README has its own runs and results.
|
|
268
346
|
|
|
347
|
+
## The skill
|
|
348
|
+
|
|
349
|
+
`npx skills add monotykamary/gdp-wired` installs a skill that guides coding agents (and people) through writing a rule
|
|
350
|
+
the way the code does it, stating its laws, reading a refused proof and handing a claim to Lean:
|
|
351
|
+
|
|
352
|
+
- [**Workflow**](skills/gdp-wired/SKILL.md#workflow): the checklist for adding or changing a rule whose invariants matter.
|
|
353
|
+
- [**Writing rules**](skills/gdp-wired/references/writing-rules.md): kinds, maps, step and k-step laws, histories,
|
|
354
|
+
liveness, decisions, two-run laws, loops.
|
|
355
|
+
- [**Reading verdicts**](skills/gdp-wired/references/reading-verdicts.md): a bug or a missing invariant, and what to
|
|
356
|
+
change.
|
|
357
|
+
- [**Shipping**](skills/gdp-wired/references/shipping.md): running the proven rule, and holding existing code to it.
|
|
358
|
+
- [**The Lean handoff**](skills/gdp-wired/references/lean-handoff.md): exporting the proofs, and functions proven in Lean.
|
|
359
|
+
- [**Where it fits**](skills/gdp-wired/references/where-it-fits.md): gdp-wired, Lean or Bend.
|
|
360
|
+
|
|
269
361
|
## Credits
|
|
270
362
|
|
|
271
363
|
[gdp-ts](https://github.com/rauchg/gdp-ts) by Guillermo Rauch (Ghosts of Departed Proofs for TypeScript) is where the
|
|
272
|
-
audit path starts; [Handwired](https://github.com/chenglou/handwired) by Cheng Lou is the circuit and GPU runtime this
|
|
364
|
+
audit path starts, and what `gdp-wired/gdp` mints proofs for; [Handwired](https://github.com/chenglou/handwired) by Cheng Lou is the circuit and GPU runtime this
|
|
273
365
|
builds on, through a fork; [Bend](https://github.com/bendlang/bend) is the language the laws were first written in,
|
|
274
366
|
and the yardstick for the benchmarks.
|
|
275
367
|
|
package/dist/core/dsl.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type C, type E, type Law, type Machine, type Request, type State } from './machine.js';
|
|
1
|
+
import { type C, type E, type Law, type Recurrence, type Machine, type Request, type State } from './machine.js';
|
|
2
2
|
import { type Goal, type Liveness, type Loop, type StepLaw, type Tree } from '../kernel/kernel.js';
|
|
3
3
|
export type TermLike = Term | number;
|
|
4
4
|
export declare const termOf: (t: TermLike) => E;
|
|
@@ -43,11 +43,17 @@ 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>;
|
|
47
|
+
export declare function when<T extends Record<string, unknown>>(c: Cond, then: T, otherwise: T): T;
|
|
46
48
|
export declare const choose: (c: Cond, then: Cond, otherwise: Cond) => Cond;
|
|
49
|
+
export declare function same(a: unknown, b: unknown): Cond;
|
|
47
50
|
export declare const count: (c: Cond) => Term;
|
|
48
51
|
export declare const sum: (first: TermLike, ...rest: TermLike[]) => Term;
|
|
49
52
|
type Rand = (k: number) => number;
|
|
50
|
-
type Raw = number | boolean | string | null | undefined;
|
|
53
|
+
export type Raw = number | boolean | string | null | undefined | RawObject | readonly Raw[];
|
|
54
|
+
export type RawObject = {
|
|
55
|
+
readonly [field: string]: Raw;
|
|
56
|
+
};
|
|
51
57
|
export type At = string | ((name: string) => string);
|
|
52
58
|
export type Init<V> = {
|
|
53
59
|
readonly kind: Kind<V>;
|
|
@@ -57,7 +63,8 @@ export declare abstract class Kind<V> {
|
|
|
57
63
|
readonly _value: V;
|
|
58
64
|
abstract vars(name: string): string[];
|
|
59
65
|
abstract assume(name: string): Cond[];
|
|
60
|
-
abstract
|
|
66
|
+
abstract build(name: string, get: (part: string) => Term): V;
|
|
67
|
+
value(name: string, at?: At): V;
|
|
61
68
|
abstract encode(name: string, raw: Raw): Record<string, number>;
|
|
62
69
|
abstract random(name: string, rand: Rand, bound: number): Record<string, number>;
|
|
63
70
|
abstract store(name: string, value: unknown): Record<string, TermLike>;
|
|
@@ -145,6 +152,61 @@ export declare class Maybe<V> {
|
|
|
145
152
|
}): Term;
|
|
146
153
|
}
|
|
147
154
|
export declare const maybe: <V>(of: Kind<V>) => Kind<Maybe<V>>;
|
|
155
|
+
type Fields = Record<string, Kind<unknown>>;
|
|
156
|
+
export declare const record: <const K extends Fields>(fields: K) => Kind<ValuesOf<K>>;
|
|
157
|
+
type Cases = Record<string, Fields>;
|
|
158
|
+
type CaseValues<C extends Cases> = {
|
|
159
|
+
[k in keyof C]: ValuesOf<C[k]>;
|
|
160
|
+
};
|
|
161
|
+
type MatchCases<C extends Cases, T> = {
|
|
162
|
+
[k in keyof C]: (fields: ValuesOf<C[k]>) => T;
|
|
163
|
+
};
|
|
164
|
+
export declare class Variant<C extends Cases = Cases> {
|
|
165
|
+
readonly tag: Term;
|
|
166
|
+
readonly cases: CaseValues<C>;
|
|
167
|
+
readonly kind: VariantKind<C>;
|
|
168
|
+
constructor(tag: Term, cases: CaseValues<C>, kind: VariantKind<C>);
|
|
169
|
+
is(k: keyof C & string): Cond;
|
|
170
|
+
match(cases: MatchCases<C, Cond>): Cond;
|
|
171
|
+
match(cases: MatchCases<C, TermLike>): Term;
|
|
172
|
+
match<T>(cases: MatchCases<C, T>): T;
|
|
173
|
+
}
|
|
174
|
+
export type VariantWrite<C extends Cases> = {
|
|
175
|
+
[k in keyof C]: {
|
|
176
|
+
[f in 'tag' | keyof C[k]]: f extends 'tag' ? k : C[k][f] extends Kind<infer V> ? Writes<V> : never;
|
|
177
|
+
};
|
|
178
|
+
}[keyof C];
|
|
179
|
+
export declare class VariantKind<C extends Cases> extends Kind<Variant<C>> {
|
|
180
|
+
readonly cases: C;
|
|
181
|
+
readonly names: (keyof C & string)[];
|
|
182
|
+
readonly tagKey = "tag";
|
|
183
|
+
constructor(cases: C);
|
|
184
|
+
index(k: string): number;
|
|
185
|
+
private payload;
|
|
186
|
+
private filled;
|
|
187
|
+
private at;
|
|
188
|
+
vars(name: string): string[];
|
|
189
|
+
assume(name: string): Cond[];
|
|
190
|
+
build(name: string, get: (part: string) => Term): Variant<C>;
|
|
191
|
+
make(write: VariantWrite<C>): Variant<C>;
|
|
192
|
+
store(name: string, value: unknown): Record<string, TermLike>;
|
|
193
|
+
decode(name: string, vars: Record<string, number>): {
|
|
194
|
+
tag: keyof C & string;
|
|
195
|
+
};
|
|
196
|
+
empty(): {
|
|
197
|
+
tag: keyof C & string;
|
|
198
|
+
};
|
|
199
|
+
encode(name: string, raw: Raw): {
|
|
200
|
+
[x: string]: number;
|
|
201
|
+
};
|
|
202
|
+
random(name: string, rand: Rand, bound: number): {
|
|
203
|
+
[x: string]: number;
|
|
204
|
+
};
|
|
205
|
+
sample(rand: Rand, bound: number): {
|
|
206
|
+
tag: keyof C & string;
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
export declare const variant: <const C extends Cases>(cases: C) => VariantKind<C>;
|
|
148
210
|
export declare const intern: ((text: string) => number) & {
|
|
149
211
|
load(entries: [string, number][]): void;
|
|
150
212
|
};
|
|
@@ -225,6 +287,42 @@ export declare class ListRef {
|
|
|
225
287
|
within(): Forall;
|
|
226
288
|
increasing(): Forall;
|
|
227
289
|
}
|
|
290
|
+
export type PartsWrite = {
|
|
291
|
+
readonly parts: Record<string, MapWrite>;
|
|
292
|
+
};
|
|
293
|
+
export declare class CellsRef<V> {
|
|
294
|
+
readonly name: string;
|
|
295
|
+
readonly kind: Kind<V>;
|
|
296
|
+
private readonly map;
|
|
297
|
+
constructor(name: string, kind: Kind<V>, map: (part: string) => MapRef);
|
|
298
|
+
at(key: TermLike): V;
|
|
299
|
+
set(key: TermLike, value: Writes<V>): PartsWrite;
|
|
300
|
+
each(f: (key: Term) => Writes<V>): PartsWrite;
|
|
301
|
+
}
|
|
302
|
+
export type ItemsOp = {
|
|
303
|
+
readonly when: Cond;
|
|
304
|
+
readonly parts: Record<string, ListOp>;
|
|
305
|
+
readonly len: TermLike;
|
|
306
|
+
};
|
|
307
|
+
export declare class ItemsRef<V> {
|
|
308
|
+
readonly name: string;
|
|
309
|
+
readonly kind: Kind<V>;
|
|
310
|
+
readonly len: Term;
|
|
311
|
+
private readonly list;
|
|
312
|
+
constructor(name: string, kind: Kind<V>, len: Term, list: (part: string) => ListRef);
|
|
313
|
+
private get names();
|
|
314
|
+
at(i: TermLike): V;
|
|
315
|
+
has(x: V): Cond;
|
|
316
|
+
private op;
|
|
317
|
+
push(x: Writes<V>, when?: Cond): ItemsOp;
|
|
318
|
+
pop(when?: Cond): ItemsOp;
|
|
319
|
+
set(i: TermLike, x: Writes<V>, when?: Cond): ItemsOp;
|
|
320
|
+
insertAt(i: TermLike, x: Writes<V>, when?: Cond): ItemsOp;
|
|
321
|
+
removeAt(i: TermLike, when?: Cond): ItemsOp;
|
|
322
|
+
apply(...ops: ItemsOp[]): Record<string, TermLike | PartsWrite>;
|
|
323
|
+
within(): Forall;
|
|
324
|
+
increasing(by: (x: V) => Term): Forall;
|
|
325
|
+
}
|
|
228
326
|
export type Forall = {
|
|
229
327
|
readonly forall: (key: Term, ...more: Term[]) => Cond;
|
|
230
328
|
};
|
|
@@ -239,7 +337,9 @@ export type FnDef = {
|
|
|
239
337
|
arity: number;
|
|
240
338
|
js: string | ((...args: number[]) => number);
|
|
241
339
|
lean?: string;
|
|
242
|
-
facts?: (f: (...args: TermLike[]) => Term) => Record<string, Cond | Forall>;
|
|
340
|
+
facts?: (f: (...args: TermLike[]) => Term, uses?: Record<string, (...args: TermLike[]) => Term>) => Record<string, Cond | Forall>;
|
|
341
|
+
recur?: Recurrence;
|
|
342
|
+
uses?: Record<string, FnDef>;
|
|
243
343
|
};
|
|
244
344
|
export declare const fn: (arity: number, js: FnDef["js"], opts?: {
|
|
245
345
|
lean?: string;
|
|
@@ -247,16 +347,35 @@ export declare const fn: (arity: number, js: FnDef["js"], opts?: {
|
|
|
247
347
|
}) => FnDef;
|
|
248
348
|
export declare const divBy: (c: number) => FnDef;
|
|
249
349
|
export declare const modBy: (c: number) => FnDef;
|
|
350
|
+
type FnTerm = (...args: TermLike[]) => Term;
|
|
351
|
+
type RecurEnv<A extends string, U> = {
|
|
352
|
+
[a in A]: Term;
|
|
353
|
+
} & {
|
|
354
|
+
[u in keyof U]: FnTerm;
|
|
355
|
+
};
|
|
356
|
+
export type RecurDef<A extends string, U extends Record<string, FnDef>> = {
|
|
357
|
+
args?: readonly A[];
|
|
358
|
+
fns?: U;
|
|
359
|
+
zero: TermLike | ((env: {
|
|
360
|
+
[a in A]: Term;
|
|
361
|
+
}) => TermLike);
|
|
362
|
+
next: (x: Term, prev: Term, env: RecurEnv<A, U>) => TermLike;
|
|
363
|
+
};
|
|
364
|
+
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 & {
|
|
365
|
+
verdict: Verdict;
|
|
366
|
+
};
|
|
250
367
|
type Kinds = Record<string, Kind<unknown>>;
|
|
251
368
|
type ValuesOf<K extends Kinds> = {
|
|
252
369
|
[n in keyof K]: K[n] extends Kind<infer V> ? V : never;
|
|
253
370
|
};
|
|
254
371
|
type Obj = object;
|
|
255
|
-
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 : TermLike
|
|
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> ? {
|
|
373
|
+
[k in keyof V]: Writes<V[k]>;
|
|
374
|
+
} : TermLike;
|
|
256
375
|
type Update<S extends Obj, M extends Obj> = Partial<{
|
|
257
376
|
[k in keyof S]: Writes<S[k]>;
|
|
258
377
|
} & {
|
|
259
|
-
[k in keyof M]: MapWrite;
|
|
378
|
+
[k in keyof M]: M[k] extends CellsRef<unknown> | ItemsRef<unknown> ? PartsWrite : MapWrite;
|
|
260
379
|
}>;
|
|
261
380
|
type Spec = {
|
|
262
381
|
name: string;
|
|
@@ -265,6 +384,8 @@ type Spec = {
|
|
|
265
384
|
state: [string, number | ((p: never) => TermLike)][];
|
|
266
385
|
maps: [string, number][];
|
|
267
386
|
lists?: string[];
|
|
387
|
+
cells?: [string, Kind<unknown>][];
|
|
388
|
+
items?: [string, Kind<unknown>][];
|
|
268
389
|
kinded?: [string, Kind<unknown>][];
|
|
269
390
|
carried?: string[];
|
|
270
391
|
fns?: [string, FnDef][];
|
|
@@ -293,8 +414,8 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
293
414
|
state<T extends Record<string, number | ((p: P) => TermLike) | Init<unknown>>>(init: T): Rule<P, I, S & {
|
|
294
415
|
[k in keyof T]: T[k] extends Init<infer V> ? V : Term;
|
|
295
416
|
}, M, F, R, Su, Q>;
|
|
296
|
-
maps<T extends Record<string, number
|
|
297
|
-
[k in keyof T]: MapRef;
|
|
417
|
+
maps<T extends Record<string, number | Init<unknown>>>(init: T): Rule<P, I, S, M & {
|
|
418
|
+
[k in keyof T]: T[k] extends Init<infer V> ? CellsRef<V> : MapRef;
|
|
298
419
|
}, F, R, Su, Q>;
|
|
299
420
|
ints<T extends Record<string, number | ((p: P) => IntLike)>>(init: T): Rule<P, I, S & {
|
|
300
421
|
[k in keyof T]: Int;
|
|
@@ -307,6 +428,11 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
307
428
|
}, M & {
|
|
308
429
|
[k in N]: ListRef;
|
|
309
430
|
}, F, R, Su, Q>;
|
|
431
|
+
items<T extends Record<string, Kind<unknown>>>(kinds: T): Rule<P, I, S & {
|
|
432
|
+
[k in keyof T & string as `${k}_len`]: Term;
|
|
433
|
+
}, M & {
|
|
434
|
+
[k in keyof T]: ItemsRef<T[k] extends Kind<infer V> ? V : never>;
|
|
435
|
+
}, F, R, Su, Q>;
|
|
310
436
|
fns<T extends Record<string, FnDef>>(defs: T): Rule<P & {
|
|
311
437
|
[k in keyof T]: (...args: TermLike[]) => Term;
|
|
312
438
|
}, I, S, M, F, R, Su, Q>;
|
|
@@ -348,6 +474,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
348
474
|
private kindAssumptions;
|
|
349
475
|
private afterEnv;
|
|
350
476
|
private env;
|
|
477
|
+
private composites;
|
|
351
478
|
private names;
|
|
352
479
|
private whatIs;
|
|
353
480
|
private distinct;
|
|
@@ -363,7 +490,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
363
490
|
};
|
|
364
491
|
private verdict;
|
|
365
492
|
private crossStep;
|
|
366
|
-
compare<St extends Record<string,
|
|
493
|
+
compare<St extends Record<string, Raw>>(reference: {
|
|
367
494
|
init: (params: {
|
|
368
495
|
[k in keyof P]: Raw;
|
|
369
496
|
}) => St;
|
|
@@ -425,13 +552,28 @@ export type Decision = {
|
|
|
425
552
|
then: C;
|
|
426
553
|
goal: Goal;
|
|
427
554
|
};
|
|
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>] ? {
|
|
556
|
+
[f in keyof V]: ResultOf<V[f]>;
|
|
557
|
+
} : Term;
|
|
428
558
|
type Result<T> = {
|
|
429
|
-
[k in keyof T]:
|
|
559
|
+
[k in keyof T]: ResultOf<T[k]>;
|
|
430
560
|
};
|
|
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>] ? {
|
|
562
|
+
[f in keyof V]: ValueOf<V[f]>;
|
|
563
|
+
} : number;
|
|
564
|
+
type CaseRaw<C extends Cases> = {
|
|
565
|
+
[k in keyof C]: {
|
|
566
|
+
tag: k;
|
|
567
|
+
} & {
|
|
568
|
+
[f in keyof C[k]]: C[k][f] extends Kind<infer V> ? ValueOf<V> : never;
|
|
569
|
+
};
|
|
570
|
+
}[keyof C];
|
|
431
571
|
type Value<T> = {
|
|
432
|
-
[k in keyof T]:
|
|
572
|
+
[k in keyof T]: ValueOf<T[k]>;
|
|
573
|
+
};
|
|
574
|
+
type Field = Term | Cond | Int | Decimal | Choice<string> | Str | Variant<any> | {
|
|
575
|
+
[f: string]: Field | number;
|
|
433
576
|
};
|
|
434
|
-
type Field = Term | Cond | Int | Decimal | Choice<string> | Str;
|
|
435
577
|
export declare class Fun<P extends Obj = {}, I extends Obj = {}, O extends Obj = {}> {
|
|
436
578
|
private readonly r;
|
|
437
579
|
private readonly out?;
|
|
@@ -469,6 +611,7 @@ export declare class Fun<P extends Obj = {}, I extends Obj = {}, O extends Obj =
|
|
|
469
611
|
}): Value<O>;
|
|
470
612
|
}
|
|
471
613
|
export declare const fun: (name: string) => Fun;
|
|
614
|
+
export declare const funOf: (d: Decision) => Fun<Obj, Obj, Obj> | undefined;
|
|
472
615
|
export type Agreement = {
|
|
473
616
|
checked: number;
|
|
474
617
|
failure?: string;
|
|
@@ -523,6 +666,10 @@ export type Verdict = {
|
|
|
523
666
|
why?: string;
|
|
524
667
|
trees?: Record<string, Tree>;
|
|
525
668
|
};
|
|
669
|
+
export declare class Unproven extends Error {
|
|
670
|
+
readonly verdict: Verdict;
|
|
671
|
+
constructor(what: string, verdict: Verdict);
|
|
672
|
+
}
|
|
526
673
|
export declare function proveRule(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[]): Verdict;
|
|
527
674
|
export declare function proveSteps(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], stepLaws: StepLaw[]): Verdict;
|
|
528
675
|
export type { StepLaw };
|