lawspec 0.10.0 → 0.12.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/API-MIGRATION.md +9 -0
- package/HASKELL.md +1 -0
- package/KOTLIN.md +5 -0
- package/LANGUAGE.md +136 -13
- package/NATIVE-BINDINGS.md +10 -5
- package/PRIMITIVES.md +1 -1
- package/README.md +38 -13
- package/REFINEMENTS.md +6 -1
- package/RELEASE-0.11.md +62 -0
- package/RELEASE-0.12.md +47 -0
- package/bin/lawspec.mjs +9 -3
- package/build.json +37 -33
- package/core.wasm +0 -0
- package/examples/specs/indexed_families.lawspec +88 -0
- package/package.json +2 -2
package/API-MIGRATION.md
CHANGED
|
@@ -278,3 +278,12 @@ Existing factory files remain untouched. Factory signature changes
|
|
|
278
278
|
use the existing `adapterUpdates` reporting channel. See `NATIVE-BINDINGS.md`
|
|
279
279
|
for grouping, module collision checks, and layout
|
|
280
280
|
migration behavior.
|
|
281
|
+
|
|
282
|
+
## Evidence in check results (0.12)
|
|
283
|
+
|
|
284
|
+
Check, expand and generation responses include an additive `evidence` array.
|
|
285
|
+
Each item names the owning unit, the contract `declaration`, the `stage`
|
|
286
|
+
(`precondition` or `postcondition`), a `status` of `proved` or
|
|
287
|
+
`runtime-checked`, a `reason`, and the `claim` as a typed expression. Schemas 3
|
|
288
|
+
and 4 are otherwise unchanged; clients that ignore unknown fields need no
|
|
289
|
+
changes. Generated definition code no longer re-checks proved postconditions.
|
package/HASKELL.md
CHANGED
|
@@ -41,6 +41,7 @@ have no constructors. They can appear in inhabited containers such as
|
|
|
41
41
|
| `Nullable a` | `LS.Nullable a`, with `NullValue` and `NullableValue` |
|
|
42
42
|
| `Optional a` | `LS.Optional a`, with `UndefinedValue` and `OptionalValue` |
|
|
43
43
|
| `BigInt`, `BigUInt`, `Integer` | `Integer`, with domain checks |
|
|
44
|
+
| `Integer` adapter result | `LS.IntegerValue`, built from any `Integral` with `LS.integerValue` |
|
|
44
45
|
| `Decimal` | `LS.Decimal`, wrapping an exact finite base-ten `Rational` |
|
|
45
46
|
| `Rational` | `Rational` |
|
|
46
47
|
| `Complex64`, `Complex128` | `Complex Float`, `Complex Double` |
|
package/KOTLIN.md
CHANGED
|
@@ -61,6 +61,11 @@ arbitrary integers, and machine-profile integers use `BigInteger` with explicit
|
|
|
61
61
|
domain checks. These portable machine-profile representations do not bind a
|
|
62
62
|
host machine-sized primitive.
|
|
63
63
|
|
|
64
|
+
An adapter whose result is the abstract `Integer` returns Kotlin `Number`.
|
|
65
|
+
`Integer` is the top of the integral tower, so an implementation may return
|
|
66
|
+
`Int`, `Long` or `BigInteger`; the result bridge rejects non-integral values
|
|
67
|
+
such as `Double` and checks the logical domain. Arguments remain `BigInteger`.
|
|
68
|
+
|
|
64
69
|
Decimal uses exact `BigDecimal`; Rational uses the normalized
|
|
65
70
|
`LawSpecRuntime.Ratio`. Complex components use `LawSpecRuntime.Complex`, with
|
|
66
71
|
Float32 precision validated for Complex64. Raw code-point text uses `IntArray`,
|
package/LANGUAGE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# LawSpec language and compiler boundary (0.
|
|
1
|
+
# LawSpec language and compiler boundary (0.12)
|
|
2
2
|
|
|
3
3
|
LawSpec describes portable laws, concrete examples, and adapter contracts. The
|
|
4
4
|
compiler is written in Haskell. Rust is an output backend alongside Java, Python,
|
|
@@ -102,7 +102,8 @@ scrutinee once; each branch binds that constructor's fields in the same order.
|
|
|
102
102
|
Bindings are scoped to the branch. Matching must be exhaustive and cannot repeat
|
|
103
103
|
a constructor. Lists match with `Nil` and `Cons head tail`; Maybe and Either use
|
|
104
104
|
their constructors above. Recursive declarations must be strictly positive.
|
|
105
|
-
|
|
105
|
+
Constructors may refine natural indices; see [indexed families](#natural-indexed-families).
|
|
106
|
+
GADT result signatures that refine type arguments are not supported.
|
|
106
107
|
|
|
107
108
|
Equality is structural and type-directed, including named fields and nested
|
|
108
109
|
containers. Native public declarations retain their names and type parameters;
|
|
@@ -129,6 +130,89 @@ of recursive and nonrecursive named type constructors are supported; see
|
|
|
129
130
|
constructor fields use checked constructor contracts. Recursive payload predicates
|
|
130
131
|
follow stored type arguments and preserve outer dependent inputs.
|
|
131
132
|
|
|
133
|
+
## Natural-indexed families
|
|
134
|
+
|
|
135
|
+
A data declaration may take `Natural` parameters. Each constructor states how it
|
|
136
|
+
determines them with `where <index> = <expression>`:
|
|
137
|
+
|
|
138
|
+
```lawspec
|
|
139
|
+
type Vec (n :: Natural) (a :: Type) is
|
|
140
|
+
| VNil where n = 0
|
|
141
|
+
| VCons head :: a tail :: Vec m a where n = m + 1
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
type Tree (n :: Natural) (a :: Type) is
|
|
145
|
+
| Tip where n = 0
|
|
146
|
+
| Bin left :: Tree l a value :: a right :: Tree r a where n = l + r + 1
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
append :: (xs :: Vec n Int8) -> (ys :: Vec m Int8) -> (r :: Vec (n + m) Int8)
|
|
150
|
+
zip :: (xs :: Vec n Int8) -> (ys :: Vec n Bool) -> (r :: Vec n Bool)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Index expressions are sums of natural literals and index variables. A variable
|
|
154
|
+
such as `m` is bound by the field whose type mentions it, and every index needs
|
|
155
|
+
exactly one equation in every constructor. `Natural` is also an ordinary value
|
|
156
|
+
type: an unbounded integer that is at least zero.
|
|
157
|
+
|
|
158
|
+
Indices are evidence, not a second type system. The compiler elaborates a family
|
|
159
|
+
before inference into three ordinary declarations:
|
|
160
|
+
|
|
161
|
+
- erased data `Vec a` with the same constructors, which is the native
|
|
162
|
+
representation on every target;
|
|
163
|
+
- a checked structural measure for each index, named `<index>Of<Type>` (here
|
|
164
|
+
`nOfVec` and `nOfTree`), recomputed from the constructor equations;
|
|
165
|
+
- a refinement, so `Vec e a` in any signature or quantifier means
|
|
166
|
+
`(v :: Vec a where nOfVec v == e)`.
|
|
167
|
+
|
|
168
|
+
`append` above is therefore an adapter contract: its result must have length
|
|
169
|
+
`nOfVec xs + nOfVec ys`, and a native implementation that drops an element
|
|
170
|
+
fails with the postcondition. An index variable that is otherwise unbound, like
|
|
171
|
+
`n` and `m` in `append`, is implicit. It is determined by the first binder whose
|
|
172
|
+
family type mentions it alone, and later occurrences read that binder's measure.
|
|
173
|
+
Implicit indices must not first appear inside an expression, and a result cannot
|
|
174
|
+
introduce one.
|
|
175
|
+
|
|
176
|
+
Generation follows the index. For a free index, as in `append`, values come from
|
|
177
|
+
the erased type and the index is their measure. For a fixed index (`Vec 3 Int8`)
|
|
178
|
+
or a shared one (`zip`'s `ys`), the target is solved backwards through the
|
|
179
|
+
constructor equations: `VCons` for `n = 3` needs a tail with index 2, and `Bin`
|
|
180
|
+
splits `n - 1` between its subtrees. Samples are constructed, not filtered, and
|
|
181
|
+
shrinking stays within the index on every target. The same planning applies to
|
|
182
|
+
any user-written measure over declared data whose branches are a constant plus
|
|
183
|
+
the same measure of that branch's fields.
|
|
184
|
+
|
|
185
|
+
### Proved indices and evidence
|
|
186
|
+
|
|
187
|
+
Checked definitions may return indexed families. The compiler proves their
|
|
188
|
+
result indices statically, so they need no runtime postcondition:
|
|
189
|
+
|
|
190
|
+
```lawspec
|
|
191
|
+
definition concatV (xs :: Vec n Int8) (ys :: Vec m Int8) :: Vec (n + m) Int8 is
|
|
192
|
+
match xs with
|
|
193
|
+
| VNil -> ys
|
|
194
|
+
| VCons h t -> VCons h (concatV t ys)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The proof uses exact linear arithmetic over the measures. A measure applied to a
|
|
200
|
+
known constructor unfolds to that constructor's equation, so `nOfVec (VCons h t)`
|
|
201
|
+
is `nOfVec t + 1`. Within a match branch, the scrutinee's constructor is known.
|
|
202
|
+
Calls of checked definitions are pure, so equal calls have equal results. A
|
|
203
|
+
recursive call contributes its own signature as the induction hypothesis, and
|
|
204
|
+
natural measures are non-negative. A definition whose result index does not
|
|
205
|
+
follow fails with `definition result refinement could not be proved`.
|
|
206
|
+
|
|
207
|
+
Every contract obligation is recorded with how it is discharged. Definition
|
|
208
|
+
postconditions are `proved`, and generated code omits their runtime checks.
|
|
209
|
+
Definition preconditions guard native callers, and adapter contracts cover
|
|
210
|
+
native code that LawSpec cannot inspect, so both are `runtime-checked`.
|
|
211
|
+
`lawspec check` summarizes the evidence, and the API reports each obligation in
|
|
212
|
+
the `evidence` field. A proved definition is a natural reference model for a
|
|
213
|
+
native adapter, as in `append xs ys = concatV xs ys` in the
|
|
214
|
+
[indexed example](examples/specs/indexed_families.lawspec).
|
|
215
|
+
|
|
132
216
|
## Total definitions
|
|
133
217
|
|
|
134
218
|
A unit can supply an implementation as a checked total definition:
|
|
@@ -332,17 +416,56 @@ API schema v3 uses separately defined wire views, with lossless tagged scalar
|
|
|
332
416
|
values. It does not serialize internal AST constructors. See the
|
|
333
417
|
[API migration guide](API-MIGRATION.md).
|
|
334
418
|
|
|
335
|
-
##
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
The Core type model distinguishes type arguments from index
|
|
339
|
-
representation is not a claim that arbitrary dependent
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
419
|
+
## Roadmap
|
|
420
|
+
|
|
421
|
+
0.11 elaborates natural-indexed families to erased data, measures and
|
|
422
|
+
refinements. The Core type model distinguishes type arguments from index
|
|
423
|
+
arguments, but that representation is not a claim that arbitrary dependent
|
|
424
|
+
programs are accepted. External type bindings and custom generator bindings
|
|
425
|
+
(0.10) configure native representations alongside the typed testing plan and do
|
|
426
|
+
not change source-language typing or equality; see
|
|
427
|
+
[the binding reference](NATIVE-BINDINGS.md).
|
|
428
|
+
|
|
429
|
+
### 0.12 Proof-producing dependent layer (released)
|
|
430
|
+
|
|
431
|
+
- Index equalities in checked definitions are discharged statically. Adapter
|
|
432
|
+
results remain runtime-checked.
|
|
433
|
+
- Linear `Natural` arithmetic over measures is solved exactly, with unfolding
|
|
434
|
+
on known constructors and induction through recursive calls.
|
|
435
|
+
- Evidence is recorded for every contract obligation as `proved` or
|
|
436
|
+
`runtime-checked`, and reported by `lawspec check` and the API.
|
|
437
|
+
- Proved definition postconditions produce no runtime checks.
|
|
438
|
+
- Checked definitions may return indexed families. See
|
|
439
|
+
[proved indices](#proved-indices-and-evidence).
|
|
440
|
+
|
|
441
|
+
Still open, for evaluation in later releases: index equalities between sibling
|
|
442
|
+
fields (perfect trees whose subtrees share one index), non-linear indices, and
|
|
443
|
+
GADTs that refine type arguments.
|
|
444
|
+
|
|
445
|
+
### 0.13 Wlaschin-style domain modeling primitives
|
|
446
|
+
|
|
447
|
+
- Semantic wrappers and constrained primitives.
|
|
448
|
+
- Make illegal states unrepresentable.
|
|
449
|
+
- Explicit domain workflows and state distinctions.
|
|
450
|
+
|
|
451
|
+
These build on 0.10 native bindings and 0.11 refinements.
|
|
452
|
+
|
|
453
|
+
### 0.14 Cross-unit imports and packages
|
|
454
|
+
|
|
455
|
+
- Reusable law, type and refinement libraries.
|
|
456
|
+
- Versioning and namespacing, including constructor names scoped to their unit.
|
|
457
|
+
Today every unit compiled together needs distinct constructor names.
|
|
458
|
+
- Publishable behavioral contracts.
|
|
459
|
+
|
|
460
|
+
### 0.15 Evidence/discharge model
|
|
461
|
+
|
|
462
|
+
Each obligation reports how it was discharged:
|
|
463
|
+
|
|
464
|
+
- `PROVED`: discharged statically, by 0.12 evidence or definition proofs.
|
|
465
|
+
- `EXHAUSTIVELY CHECKED`: every value of a finite domain was checked.
|
|
466
|
+
- `PROPERTY TESTED`: generated cases, examples and boundaries.
|
|
467
|
+
- `RUNTIME CHECKED`: adapter contracts and checked codecs at native boundaries.
|
|
468
|
+
- `ASSUMED / EXTERNAL`: native adapters and bindings taken on trust.
|
|
346
469
|
|
|
347
470
|
## Generated project formatting
|
|
348
471
|
|
package/NATIVE-BINDINGS.md
CHANGED
|
@@ -973,7 +973,10 @@ Gradle's coordination socket. The saved logs confirm successful native tests,
|
|
|
973
973
|
seven expected failures among 45 tests for the deliberately incorrect fee, and
|
|
974
974
|
zero planned regeneration changes in both configurations. This verifies the
|
|
975
975
|
installed CLI/Gradle path as well as the separate compilation/runtime harness.
|
|
976
|
-
The CI additions
|
|
976
|
+
The remote run of these CI additions exposed failures in the Kotlin and Haskell
|
|
977
|
+
target jobs. They came from the 0.9 narrowing of abstract `Integer` adapter
|
|
978
|
+
results, not from bindings, and are fixed in 0.11
|
|
979
|
+
([release notes](RELEASE-0.11.md#tower-polymorphic-integer-results-restored)).
|
|
977
980
|
|
|
978
981
|
The runnable assets live in `examples/native-payments/` and are copied into the
|
|
979
982
|
npm package by `tools/wasm.sh`. The original `lawspec examples` command retains its
|
|
@@ -996,8 +999,8 @@ The full npm regression suite passes 77 tests, and the package smoke test passes
|
|
|
996
999
|
installed API generation on all eight targets plus executable Rust scaffold,
|
|
997
1000
|
doctor, adapter and regeneration checks. The package contains the binding guide,
|
|
998
1001
|
migration notes, release notes and all eight application example configurations.
|
|
999
|
-
|
|
1000
|
-
|
|
1002
|
+
The remote CI gate for these checks was reviewed during the 0.11 release, which
|
|
1003
|
+
re-ran every target job, including both installed native-binding profiles.
|
|
1001
1004
|
|
|
1002
1005
|
The empty-parameter audit must distinguish an empty type from an inhabited type
|
|
1003
1006
|
that mentions it. A native factory for `Phantom Empty` may ignore its child
|
|
@@ -1042,5 +1045,7 @@ after the empty-parameter changes.
|
|
|
1042
1045
|
regeneration protection and packaged installation remain tested.
|
|
1043
1046
|
- Python follows PEP 8; Rust remains a first-class acceptance target throughout.
|
|
1044
1047
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1048
|
+
Dependent indices shipped as natural-indexed families in 0.11. Proof-producing
|
|
1049
|
+
indices and cross-unit packages are 0.12 and 0.14 on the
|
|
1050
|
+
[roadmap](LANGUAGE.md#roadmap). They are not prerequisites for binding ordinary
|
|
1051
|
+
0.9 products and sums.
|
package/PRIMITIVES.md
CHANGED
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**State the law once. Check it everywhere.**
|
|
4
4
|
|
|
5
|
-
LawSpec 0.
|
|
5
|
+
LawSpec 0.12 compiles reusable laws into native property tests, executable examples,
|
|
6
6
|
and implementation adapters. The compiler is Haskell, distributed as prebuilt
|
|
7
7
|
WebAssembly with a Node CLI and an asynchronous, typed JavaScript API.
|
|
8
8
|
|
|
@@ -47,6 +47,31 @@ the [release notes](RELEASE-0.10.md), and
|
|
|
47
47
|
[schema 4 migration notes](API-MIGRATION.md#schema-4-native-bindings).
|
|
48
48
|
Existing specifications without bindings retain their behavior.
|
|
49
49
|
|
|
50
|
+
## Natural-indexed families in 0.11
|
|
51
|
+
|
|
52
|
+
LawSpec 0.11 adds data indexed by natural numbers, such as length-indexed
|
|
53
|
+
vectors and size-indexed trees. Each constructor states its index equation;
|
|
54
|
+
the compiler elaborates the family to ordinary native data, a checked measure,
|
|
55
|
+
and a refinement. Dependent signatures like
|
|
56
|
+
`append :: (xs :: Vec n a) -> (ys :: Vec m a) -> (r :: Vec (n + m) a)` become
|
|
57
|
+
checked adapter contracts, and generators construct values with a required
|
|
58
|
+
index instead of filtering for it. See the
|
|
59
|
+
[language reference](LANGUAGE.md#natural-indexed-families), the
|
|
60
|
+
[indexed example](examples/specs/indexed_families.lawspec), and the
|
|
61
|
+
[release notes](RELEASE-0.11.md).
|
|
62
|
+
|
|
63
|
+
## Proved indices and evidence in 0.12
|
|
64
|
+
|
|
65
|
+
LawSpec 0.12 proves the result indices of checked definitions statically, so a
|
|
66
|
+
definition such as `concatV :: Vec n Int8 -> Vec m Int8 -> Vec (n + m) Int8`
|
|
67
|
+
needs no runtime postcondition. Every contract obligation is reported as
|
|
68
|
+
`proved` or `runtime-checked` by `lawspec check` and the API. See
|
|
69
|
+
[proved indices](LANGUAGE.md#proved-indices-and-evidence) and the
|
|
70
|
+
[release notes](RELEASE-0.12.md).
|
|
71
|
+
|
|
72
|
+
Planned: 0.13 domain modeling primitives, 0.14 cross-unit packages, and 0.15
|
|
73
|
+
evidence reporting. See the [roadmap](LANGUAGE.md#roadmap).
|
|
74
|
+
|
|
50
75
|
## Install and try it
|
|
51
76
|
|
|
52
77
|
LawSpec has its own [syntax-highlighting grammar and VS Code extension](editors/vscode/README.md)
|
|
@@ -56,7 +81,7 @@ support to use it; GitHub currently uses the Haskell fallback.
|
|
|
56
81
|
Install [LawSpec from npm](https://www.npmjs.com/package/lawspec):
|
|
57
82
|
|
|
58
83
|
```sh
|
|
59
|
-
npm install --save-dev lawspec@0.
|
|
84
|
+
npm install --save-dev lawspec@0.12.0
|
|
60
85
|
npx lawspec --version
|
|
61
86
|
```
|
|
62
87
|
|
|
@@ -70,8 +95,8 @@ local dependencies so LawSpec can create its `package.json` and test script:
|
|
|
70
95
|
```sh
|
|
71
96
|
mkdir lawspec-example
|
|
72
97
|
cd lawspec-example
|
|
73
|
-
npm exec --package=lawspec@0.
|
|
74
|
-
npm install --save-dev lawspec@0.
|
|
98
|
+
npm exec --package=lawspec@0.12.0 -- lawspec init --target javascript
|
|
99
|
+
npm install --save-dev lawspec@0.12.0
|
|
75
100
|
npx lawspec check
|
|
76
101
|
npx lawspec explain 'example.atoi_codec::itoa and then atoi yields a'
|
|
77
102
|
npx lawspec doctor
|
|
@@ -281,7 +306,7 @@ reusable laws accept these curried functions, their partial applications, and
|
|
|
281
306
|
scalar parameters. Quantified test inputs remain scalar.
|
|
282
307
|
|
|
283
308
|
The prelude defines the following laws. Every row has an executable example in
|
|
284
|
-
[algebra.lawspec](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
309
|
+
[algebra.lawspec](https://github.com/brain-fuel/lawspec/blob/v0.12.0/examples/specs/algebra.lawspec), including both sides of every
|
|
285
310
|
combined law. `f` and `g` are binary operations, `inverse` is unary, and `e` and
|
|
286
311
|
`zero` are scalar parameters. All these laws require equality of the element type.
|
|
287
312
|
|
|
@@ -349,7 +374,7 @@ their entire consequence. Every conjunct is type-checked and emitted. As with
|
|
|
349
374
|
existing assertions, the first failure stops that individual test. `and` is now
|
|
350
375
|
a reserved word.
|
|
351
376
|
|
|
352
|
-
[Currying examples](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
377
|
+
[Currying examples](https://github.com/brain-fuel/lawspec/blob/v0.12.0/examples/specs/currying.lawspec) demonstrate a four-argument
|
|
353
378
|
function partially applied twice, a formatter with four heterogeneous arguments,
|
|
354
379
|
and composition after partial application. Each example states its exact outputs.
|
|
355
380
|
Run `node npm/bin/lawspec.mjs examples` after rebuilding to inspect all nine units
|
|
@@ -401,7 +426,7 @@ law `valid ports round trip` is
|
|
|
401
426
|
end
|
|
402
427
|
```
|
|
403
428
|
|
|
404
|
-
The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
429
|
+
The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.12.0/examples/specs/parse_port.lawspec)
|
|
405
430
|
defines valid ports as 1–65535, and covers both endpoints, ordinary ports, zero,
|
|
406
431
|
negative values, and 65536. All explicit `expect` assertions run regardless of
|
|
407
432
|
the law's condition. A false condition skips only the consequence: invalid ports
|
|
@@ -419,7 +444,7 @@ The prelude includes `satisfies predicate` (the predicate holds for every input)
|
|
|
419
444
|
and `left inverse when predicate parse render` (the guarded round trip above).
|
|
420
445
|
These reusable laws preserve the condition and its lexical bindings when expanded.
|
|
421
446
|
`equivalent` can also compare two predicates, since `Bool` supports equality.
|
|
422
|
-
The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
447
|
+
The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.12.0/examples/specs/boolean_flags.lawspec)
|
|
423
448
|
checks that flipping twice restores both `false` and `true`; all targets generate
|
|
424
449
|
Boolean property inputs and explicit tests for both Boolean boundary values. Java caps Boolean-only JetCheck runs at the number of
|
|
425
450
|
possible input combinations (up to 100), avoiding generator exhaustion.
|
|
@@ -498,7 +523,7 @@ This expands to `for all (x :: Int32) . render (x) = referenceRender (x)`.
|
|
|
498
523
|
The example inherits the input name `x` from the prelude. Both functions are
|
|
499
524
|
user-owned adapter functions; either may delegate to your existing code.
|
|
500
525
|
|
|
501
|
-
[The complete example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
526
|
+
[The complete example](https://github.com/brain-fuel/lawspec/blob/v0.12.0/examples/specs/equivalent.lawspec) compares decimal
|
|
502
527
|
renderers and two implementations that clamp negative integers to zero. For
|
|
503
528
|
JavaScript, their adapters can be:
|
|
504
529
|
|
|
@@ -535,7 +560,7 @@ law `normalizers agree` is
|
|
|
535
560
|
end
|
|
536
561
|
```
|
|
537
562
|
|
|
538
|
-
The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
563
|
+
The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.12.0/examples/specs/slug.lawspec)
|
|
539
564
|
compares two implementations of ASCII-space replacement. It includes empty,
|
|
540
565
|
Unicode and escaped text. Each target uses its native string generator:
|
|
541
566
|
JetCheck `Generator.stringsOf(Generator.asciiPrintableChars())`, Hypothesis `st.text()`, fast-check `fc.string()`,
|
|
@@ -562,7 +587,7 @@ law `canonicalization reaches a fixed point` is
|
|
|
562
587
|
end
|
|
563
588
|
```
|
|
564
589
|
|
|
565
|
-
The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
590
|
+
The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.12.0/examples/specs/canonical_url.lawspec)
|
|
566
591
|
uses removal of **all trailing slashes** as a small fixed-point demonstration,
|
|
567
592
|
not a complete URL canonicalization algorithm. For JavaScript:
|
|
568
593
|
|
|
@@ -571,7 +596,7 @@ export const canonicalize = value => value.replace(/\/+$/, "");
|
|
|
571
596
|
```
|
|
572
597
|
|
|
573
598
|
Removing just one trailing slash fails the supplied repeated-slash example.
|
|
574
|
-
The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
599
|
+
The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.12.0/examples/specs/mixed_inputs.lawspec)
|
|
575
600
|
shows `Text` and `Int32` in the same quantified property and executable example.
|
|
576
601
|
The JavaScript API represents example bindings as typed records whose values use
|
|
577
602
|
`DataValue`: lossless tagged scalar payloads or structural constructors. Expected results are
|
|
@@ -654,7 +679,7 @@ by the JS shim.
|
|
|
654
679
|
## Build and verify
|
|
655
680
|
|
|
656
681
|
For contributors working from a repository checkout, build a local archive with
|
|
657
|
-
`npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.
|
|
682
|
+
`npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.12.0.tgz`.
|
|
658
683
|
The package payload lives in `npm/`.
|
|
659
684
|
|
|
660
685
|
```sh
|
package/REFINEMENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Refinements and abstract integers (0.
|
|
1
|
+
# Refinements and abstract integers (0.12.0)
|
|
2
2
|
|
|
3
3
|
A refinement restricts a scalar domain with a pure Boolean expression. LawSpec
|
|
4
4
|
checks concrete examples, generates satisfying input tuples, and checks adapter
|
|
@@ -154,6 +154,11 @@ seeds direct comparison values and boundaries, and checks the complete predicate
|
|
|
154
154
|
Predicates outside that analysis use bounded sampling. Small finite base-domain
|
|
155
155
|
products are enumerated exhaustively, retaining only satisfying tuples.
|
|
156
156
|
|
|
157
|
+
A refinement `m x == e` over declared data, where `m` is a linear structural
|
|
158
|
+
measure (each branch is a constant plus `m` of that branch's fields), is solved
|
|
159
|
+
rather than sampled: values are constructed with measure exactly `e`. Indexed
|
|
160
|
+
families rely on this; see [natural-indexed families](LANGUAGE.md#natural-indexed-families).
|
|
161
|
+
|
|
157
162
|
Shrinking checks refinements again and repairs dependent later inputs when an
|
|
158
163
|
earlier value changes. An overflowing counterexample can shrink to `(1, 127)`;
|
|
159
164
|
it cannot shrink to `(0, 127)` because that pair is outside the domain.
|
package/RELEASE-0.11.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# LawSpec 0.11.0
|
|
2
|
+
|
|
3
|
+
## Natural-indexed families
|
|
4
|
+
|
|
5
|
+
Data declarations may take `Natural` parameters, and each constructor states
|
|
6
|
+
its index equation:
|
|
7
|
+
|
|
8
|
+
```lawspec
|
|
9
|
+
type Vec (n :: Natural) (a :: Type) is
|
|
10
|
+
| VNil where n = 0
|
|
11
|
+
| VCons head :: a tail :: Vec m a where n = m + 1
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
append :: (xs :: Vec n Int8) -> (ys :: Vec m Int8) -> (r :: Vec (n + m) Int8)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
A family elaborates before inference into ordinary erased data, a checked
|
|
18
|
+
structural measure per index (`nOfVec`), and a refinement relating the measure
|
|
19
|
+
to the index. Core and all eight emitters are unchanged. Native code works with
|
|
20
|
+
the erased type, and every index claim is evidence checked at test time: a
|
|
21
|
+
dependent result such as `Vec (n + m) Int8` is an adapter postcondition.
|
|
22
|
+
|
|
23
|
+
An index variable that is otherwise unbound is implicit. The first binder whose
|
|
24
|
+
family type mentions it determines it, and later occurrences read that binder's
|
|
25
|
+
measure. Index expressions are sums of natural literals and index variables.
|
|
26
|
+
`Natural` is also available as a value type. Diagnostics use the `indexed` code.
|
|
27
|
+
|
|
28
|
+
## Index-directed generation
|
|
29
|
+
|
|
30
|
+
A quantifier constrained by a linear structural measure, including a fixed index
|
|
31
|
+
(`Vec 3 Int8`) or a shared one (`zip`'s second argument), is generated by solving
|
|
32
|
+
the constructor equations backwards instead of filtering. Multi-field equations
|
|
33
|
+
such as `Bin … where n = l + r + 1` split the remaining index across fields.
|
|
34
|
+
Python, JavaScript, TypeScript, Java, Kotlin, Go, Haskell and Rust construct these
|
|
35
|
+
values natively, and framework shrinking stays within the index. The same
|
|
36
|
+
planning applies to user-written linear measures over declared data.
|
|
37
|
+
|
|
38
|
+
`examples/specs/indexed_families.lawspec` runs on all eight targets through
|
|
39
|
+
`tools/indexed-integration.mjs`. Correct adapters pass free, fixed and shared
|
|
40
|
+
index laws; mutants that add, drop or lose elements fail their dependent
|
|
41
|
+
contracts.
|
|
42
|
+
|
|
43
|
+
## Tower-polymorphic Integer results restored
|
|
44
|
+
|
|
45
|
+
0.9 had narrowed Kotlin and Haskell adapters whose result is the abstract
|
|
46
|
+
`Integer` to `BigInteger` and `Integer`. They again return `Number` (Kotlin) and
|
|
47
|
+
`LS.IntegerValue` (Haskell), as in 0.8 and as Java always did. `Integer` is the top
|
|
48
|
+
of the integral tower: an implementation may return any integral native value,
|
|
49
|
+
and the result bridge rejects non-integral values and checks the logical domain.
|
|
50
|
+
Adapters written against 0.9 or 0.10 stubs that return `BigInteger` or `Integer`
|
|
51
|
+
still compile, because both are integral.
|
|
52
|
+
|
|
53
|
+
This regression had kept the Kotlin and Haskell CI target jobs failing since
|
|
54
|
+
0.9. Those jobs, and the scalar mutation fixtures, now pass with the documented
|
|
55
|
+
typed Symbol, Decimal, Utf16Text and Optional representations.
|
|
56
|
+
|
|
57
|
+
## Compatibility
|
|
58
|
+
|
|
59
|
+
Specifications without indexed families generate the same code as 0.10, apart
|
|
60
|
+
from the Integer result signatures above. API schemas 3 and 4 are unchanged.
|
|
61
|
+
GADTs that refine type arguments, non-linear indices and index equalities between
|
|
62
|
+
sibling fields remain future work.
|
package/RELEASE-0.12.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# LawSpec 0.12.0
|
|
2
|
+
|
|
3
|
+
## Proved indices
|
|
4
|
+
|
|
5
|
+
Checked definitions may return natural-indexed families, and the compiler
|
|
6
|
+
proves their result indices statically:
|
|
7
|
+
|
|
8
|
+
```lawspec
|
|
9
|
+
definition concatV (xs :: Vec n Int8) (ys :: Vec m Int8) :: Vec (n + m) Int8 is
|
|
10
|
+
match xs with
|
|
11
|
+
| VNil -> ys
|
|
12
|
+
| VCons h t -> VCons h (concatV t ys)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The totality prover now treats calls of checked definitions as pure linear
|
|
18
|
+
atoms, so equal calls have equal results. A single-argument definition that
|
|
19
|
+
matches on its argument unfolds on a known constructor, so `nOfVec (VCons h t)`
|
|
20
|
+
is `nOfVec t + 1`. Natural measures are non-negative. Together with the existing
|
|
21
|
+
match facts and the induction hypothesis from recursive calls, this proves
|
|
22
|
+
definitions such as `concatV`, and tree flattening through nested calls. It
|
|
23
|
+
rejects definitions whose result indices do not follow, including an unchanged
|
|
24
|
+
length, a dropped element, an extra element and a wrong recursive argument.
|
|
25
|
+
|
|
26
|
+
## Evidence
|
|
27
|
+
|
|
28
|
+
Every contract obligation is recorded as `proved` or `runtime-checked`.
|
|
29
|
+
Definition postconditions are proved, and generated code on all eight targets no
|
|
30
|
+
longer re-checks them at runtime. Definition preconditions, which guard native
|
|
31
|
+
callers, and adapter contracts remain runtime-checked. `lawspec check` prints a
|
|
32
|
+
summary, and API responses include an additive `evidence` array; see
|
|
33
|
+
[API migration](API-MIGRATION.md#evidence-in-check-results-012).
|
|
34
|
+
|
|
35
|
+
The indexed example now includes proved definitions `concatV` and `flattenV`,
|
|
36
|
+
used as reference models for the native `append` and `flatten` adapters. The
|
|
37
|
+
eight-target acceptance runner executes them in both machine profiles and still
|
|
38
|
+
rejects every index-breaking adapter mutant.
|
|
39
|
+
|
|
40
|
+
## Compatibility
|
|
41
|
+
|
|
42
|
+
Specifications that compiled with 0.11 compile unchanged. Generated definition
|
|
43
|
+
code omits postcondition checks that are now proved, which changes generated
|
|
44
|
+
output but not behavior. API schemas 3 and 4 are unchanged apart from the
|
|
45
|
+
additive `evidence` field. Index equalities between sibling fields, non-linear
|
|
46
|
+
indices and type-refining GADTs remain open; see the
|
|
47
|
+
[roadmap](LANGUAGE.md#roadmap).
|
package/bin/lawspec.mjs
CHANGED
|
@@ -173,13 +173,13 @@ async function main() {
|
|
|
173
173
|
}
|
|
174
174
|
if (!verb || ["help", "--help", "-h"].includes(verb)) {
|
|
175
175
|
output(
|
|
176
|
-
"LawSpec 0.
|
|
176
|
+
"LawSpec 0.12.0\nUsage: lawspec init --target <language> [--project <directory>] [--minify]\n lawspec check | doctor | explain <unit>::<law> | generate\n lawspec examples [--example payments] [--target <language>] [--output <directory>]\nOptions: --config <path>, --target <language>, --machine-bits <32|64>, --json\nGeneration: --dry-run, --check, --minify\nTargets: " +
|
|
177
177
|
targets.join(", "),
|
|
178
178
|
);
|
|
179
179
|
return;
|
|
180
180
|
}
|
|
181
181
|
if (verb === "--version") {
|
|
182
|
-
output("0.
|
|
182
|
+
output("0.12.0");
|
|
183
183
|
return;
|
|
184
184
|
}
|
|
185
185
|
if (positional.length > (verb === "explain" ? 1 : 0))
|
|
@@ -257,6 +257,12 @@ async function main() {
|
|
|
257
257
|
if (config.machineBits !== undefined && ![32, 64].includes(config.machineBits)) throw new Error("machineBits must be 32 or 64");
|
|
258
258
|
const input = { sources: await sources(config), generation: config.generation, machineBits: options.machineBits ?? config.machineBits ?? 64 };
|
|
259
259
|
const compiler = await createCompiler();
|
|
260
|
+
// Proved obligations need no runtime check; the rest are enforced at runtime.
|
|
261
|
+
const evidenceSummary = (evidence) => {
|
|
262
|
+
if (!evidence.length) return "";
|
|
263
|
+
const count = (status) => evidence.filter((item) => item.status === status).length;
|
|
264
|
+
return ` Evidence: ${count("proved")} proved, ${count("runtime-checked")} runtime-checked obligation(s).`;
|
|
265
|
+
};
|
|
260
266
|
if (verb === "check") {
|
|
261
267
|
const result = diagnostics(await compiler.check(input));
|
|
262
268
|
for (const target of selected) {
|
|
@@ -266,7 +272,7 @@ async function main() {
|
|
|
266
272
|
output(
|
|
267
273
|
options.json
|
|
268
274
|
? result
|
|
269
|
-
: `Checked ${result.laws.length} law(s)
|
|
275
|
+
: `Checked ${result.laws.length} law(s).` + evidenceSummary(result.evidence ?? []),
|
|
270
276
|
);
|
|
271
277
|
return;
|
|
272
278
|
}
|
package/build.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"src/LawSpec/Core/DefinitionContracts.hs",
|
|
40
40
|
"src/LawSpec/Core/Definitions.hs",
|
|
41
41
|
"src/LawSpec/Core/Eval.hs",
|
|
42
|
+
"src/LawSpec/Core/Evidence.hs",
|
|
42
43
|
"src/LawSpec/Core/Expression.hs",
|
|
43
44
|
"src/LawSpec/Core/Payload.hs",
|
|
44
45
|
"src/LawSpec/Core/PayloadPlan.hs",
|
|
@@ -75,6 +76,7 @@
|
|
|
75
76
|
"src/LawSpec/HaskellProperties.hs",
|
|
76
77
|
"src/LawSpec/HaskellTestHelpers.hs",
|
|
77
78
|
"src/LawSpec/HaskellTypeRefs.hs",
|
|
79
|
+
"src/LawSpec/Indexed.hs",
|
|
78
80
|
"src/LawSpec/Inference.hs",
|
|
79
81
|
"src/LawSpec/JavaData.hs",
|
|
80
82
|
"src/LawSpec/JavaDefinitions.hs",
|
|
@@ -126,23 +128,23 @@
|
|
|
126
128
|
"wasm/lawspec-wasm.cabal"
|
|
127
129
|
],
|
|
128
130
|
"digests": {
|
|
129
|
-
"lawspec.cabal": "
|
|
130
|
-
"package.yaml": "
|
|
131
|
+
"lawspec.cabal": "dee4969ade96a065c1a527c557575d4041eedd3ae664eb01ec6b568130c5a1fd",
|
|
132
|
+
"package.yaml": "e92c582f87b1704f80d5e31af7fb6dbe4f56190b8660b55cdb8728ca77efec67",
|
|
131
133
|
"runtime/LawSpecCodecs.hs": "90cb1ed15d7ab664a17a054580c849408d77cfd2b2adcb4a3228b05132caeaca",
|
|
132
|
-
"runtime/LawSpecDataStrategies.hs": "
|
|
133
|
-
"runtime/LawSpecDataStrategies.java": "
|
|
134
|
+
"runtime/LawSpecDataStrategies.hs": "cbe9f1946228db1076c19cd3158e8798fe32766cfdbea3f66b63d22d3d217508",
|
|
135
|
+
"runtime/LawSpecDataStrategies.java": "e3416efe8a0d7b1276cb4d1a8105fd01b1f5f3f02a803bf55d955d9e3ec39095",
|
|
134
136
|
"runtime/LawSpecKotlin.kt": "0cf3b7de0406155225ccca33ee3dfdf8f0456c47d577f6be8f1265c13df838cb",
|
|
135
137
|
"runtime/LawSpecKotlinCodecs.kt": "6210f77dbbe24ba2088db48dae5415abf0993fe552fb3a57d29430807efbc297",
|
|
136
|
-
"runtime/LawSpecKotlinStrategies.kt": "
|
|
138
|
+
"runtime/LawSpecKotlinStrategies.kt": "c07ef4da5ccdab9045f50f08dbfcc4a7349a505af76bd5d3ddc8c00e879437f3",
|
|
137
139
|
"runtime/LawSpecRuntime.hs": "5ced6a6a86a892ae63a544d4c4a4429bf756a730ea786478d820c8070cc96402",
|
|
138
140
|
"runtime/LawSpecRuntime.java": "c7b7cfc9b7c22b082edee14a52d95ef80ebe01ee56988c22702f629ea24b8a4f",
|
|
139
141
|
"runtime/LawSpecSchema.hs": "9912d9f525426a598f9c03c29cfc7bacf80218f73c9d537d8c1257bae245aa33",
|
|
140
142
|
"runtime/LawSpecSchema.java": "885f5f17a9ea1bdb86915dc184aea25b72c3062d6ddb9605530310540b1cc166",
|
|
141
143
|
"runtime/LawSpecStrategies.kt": "569f7775c0ac44765d05567cbf62d026558ac193c080ca71681ec6ee49c21794",
|
|
142
144
|
"runtime/lawspec_codecs.go": "b3024fe60b3b66df9ebffe995ab5bb0294a609a82c611d54957e092eac29f2f5",
|
|
143
|
-
"runtime/lawspec_data_strategies.go": "
|
|
144
|
-
"runtime/lawspec_data_strategies.mjs": "
|
|
145
|
-
"runtime/lawspec_data_strategies.py": "
|
|
145
|
+
"runtime/lawspec_data_strategies.go": "ec151dbdd1af89f57ce11c10d057d4bbc44691b0472035e0756ced1e0769a640",
|
|
146
|
+
"runtime/lawspec_data_strategies.mjs": "e74a508bf2cd382ad1ca6bc90b6be19b10170e6261dbb902f58522fa2880c2fc",
|
|
147
|
+
"runtime/lawspec_data_strategies.py": "3469bab1af47b5fb9583569d0a07f3c2a0485303d79347afe509cf34b6a21296",
|
|
146
148
|
"runtime/lawspec_runtime.go": "af6ef660387b625b3bb5fcd57f06260c821d4f65eda158db44ad12d66e1d70f8",
|
|
147
149
|
"runtime/lawspec_runtime.mjs": "3b7877c1ee523afba30ed24cd452e2a6126d9e741c7675600e867600d7a73f46",
|
|
148
150
|
"runtime/lawspec_runtime.py": "2f780bc92762eb1115f734a36cc224ae34504929b4ff0ec506735cfa9e3ab02b",
|
|
@@ -150,9 +152,9 @@
|
|
|
150
152
|
"runtime/lawspec_schema.go": "bdfb6f2552bfccf56073d52f7cdbe1d784cba2d956c29762d77df7ad6bba8d67",
|
|
151
153
|
"runtime/lawspec_schema.mjs": "60eb4204461eaa2c207fc7c3394014e2a814e4383b7045c065b4dae7c7a202c2",
|
|
152
154
|
"runtime/lawspec_schema.py": "b6967ef1a33fe6a1a180d5573ec37cd8bb2079c06e961ac2eeb05f23cffcdd07",
|
|
153
|
-
"runtime/lawspec_strategies.rs": "
|
|
154
|
-
"runtime/rust/Cargo.lock": "
|
|
155
|
-
"runtime/rust/Cargo.toml": "
|
|
155
|
+
"runtime/lawspec_strategies.rs": "cac9ca301d4614fff9acfe61e7cc5678fabc044edadfa89ec11599b6780dc5f3",
|
|
156
|
+
"runtime/rust/Cargo.lock": "614c41fad8f762414ea0e6b1de325e0e919f2f7c6a88d85eaa00ae238dc692d5",
|
|
157
|
+
"runtime/rust/Cargo.toml": "5789d98b6ed2022abf63a4a39aa155981b7819df8d41d86e354ffa7b015bb00b",
|
|
156
158
|
"src/LawSpec/Api.hs": "392f6e8c1a9ae81a95e219ac071e5b4bea03ee330d63b829036f8f54b2eda8a7",
|
|
157
159
|
"src/LawSpec/Backend.hs": "03c3b0cd62467ca677c88d29d7b12b2ab505cc0076d93d1f48fbddb0e9bcf133",
|
|
158
160
|
"src/LawSpec/Capabilities.hs": "e43d4fc3a4481fbb8aa3ff1c3f41f7e7fca5db01bfe256a30162a45d75a00c1c",
|
|
@@ -164,6 +166,7 @@
|
|
|
164
166
|
"src/LawSpec/Core/DefinitionContracts.hs": "b7f60f038b231187ecf898e585ae1a8fb1fc113bb55bad6d1bb2660350fd969e",
|
|
165
167
|
"src/LawSpec/Core/Definitions.hs": "52f024fca59661142f70f9aa7dd2cbdb4892e5d3b1ca8079c878c5a1ba667112",
|
|
166
168
|
"src/LawSpec/Core/Eval.hs": "e1ad8af5b240dd99c3b1126408d2be5f9b42b86ca6189ecd3868680a5119d931",
|
|
169
|
+
"src/LawSpec/Core/Evidence.hs": "de5b2fe0df0d681a2d6430a8c535293d7e084fe287ab47522dc57d2be857c6bf",
|
|
167
170
|
"src/LawSpec/Core/Expression.hs": "84985f4f685126253d3af7b6b605d80d28fc9aa115bd5e2946f989eeb1355224",
|
|
168
171
|
"src/LawSpec/Core/Payload.hs": "df6a2b250c27c12bb6ac16b84847c52546081d066a3dca7575ed0d743cc13d50",
|
|
169
172
|
"src/LawSpec/Core/PayloadPlan.hs": "f2a7d2dc58c6a0f79707bdc24773bd3aad3a3b8eea2735a8284b2a9f3a4af4a8",
|
|
@@ -171,13 +174,13 @@
|
|
|
171
174
|
"src/LawSpec/Core/Schema.hs": "9a4ec3967803913e6ff43d33e00a1b4b3b8ec0c79dbdbd80ae98101c85bf430f",
|
|
172
175
|
"src/LawSpec/Core/Semantics.hs": "a9d019433e3b9731f8cd0f65ec0ccc53179824bea58247b72df26760b878c2b6",
|
|
173
176
|
"src/LawSpec/Core/Total.hs": "95d2b19ba7fbfd033295504d31f786815384003b3b4d370776860b423cf079dd",
|
|
174
|
-
"src/LawSpec/Core/Totality.hs": "
|
|
177
|
+
"src/LawSpec/Core/Totality.hs": "a96fc3b9e8be5d18997065b9d09c0f8a2ede4d6ed2974b3bf355967986b0e459",
|
|
175
178
|
"src/LawSpec/Core/Types.hs": "48ed1d197ce7089737895b7279f93437301fcfacff8b3a8dd98ecd7f1cb32b37",
|
|
176
179
|
"src/LawSpec/Core/Validate.hs": "0f045241215e068081d6cd7cf451f0166671bae5fffafda71a2e2320e8014c27",
|
|
177
180
|
"src/LawSpec/Core/Value.hs": "f2b2e4d82491e184d60b1217922b25c57a06cbbaecb2cb6f31174af50268fbc9",
|
|
178
181
|
"src/LawSpec/CoreEmit.hs": "075f06ef1be2437542967b171c535686f33ed4645f6525a93c1024a9fbf68582",
|
|
179
|
-
"src/LawSpec/CoreNativeScalarEmit.hs": "
|
|
180
|
-
"src/LawSpec/CoreScalarEmit.hs": "
|
|
182
|
+
"src/LawSpec/CoreNativeScalarEmit.hs": "4082661ba5f8b1b09b4811f62ba767fa1ea401a38dd10011aea442c5a27892af",
|
|
183
|
+
"src/LawSpec/CoreScalarEmit.hs": "d1a51f7fe9b23817eeac7b50dac70ec02219df66d4b0218c5c9ae5237da244fd",
|
|
181
184
|
"src/LawSpec/Data.hs": "9dbeafa120276b114dad790ec69e617f7ad0c632e3c718962f2f7faf8ca327f4",
|
|
182
185
|
"src/LawSpec/DefinitionTotality.hs": "7671b7efb0cac5d796b074d48b2c10940e5bd0b5df082b926aee694903285efa",
|
|
183
186
|
"src/LawSpec/Domain.hs": "222d15f0012ae630802238a6d7e1ee78d1e718b599f5ebb5a1ddc4e8d29d68c3",
|
|
@@ -187,60 +190,61 @@
|
|
|
187
190
|
"src/LawSpec/Frontend.hs": "fbf091b9c96f5b5361dbf4f65819634c0f8526ff98550d97c434c0d47af7839e",
|
|
188
191
|
"src/LawSpec/Gen.hs": "c76d00fb1f7e9ed2eed7d387f1cd86cf4d12cc57f007abb0e479043b0ed377e6",
|
|
189
192
|
"src/LawSpec/GoData.hs": "35efbfdc1079e777d4760069fa2405a9700bbabab7dc7e3c2c5e0757312ea201",
|
|
190
|
-
"src/LawSpec/GoDefinitions.hs": "
|
|
193
|
+
"src/LawSpec/GoDefinitions.hs": "3cb55c22f28443fff73e73c59780bf1977ab9260b8d66c7f19d1b94b40d1397e",
|
|
191
194
|
"src/LawSpec/GoExpr.hs": "da3a7cc0e3d2906886dc44f6b990e72098bb351558bbb31ee335da7382fac58a",
|
|
192
195
|
"src/LawSpec/GoNativeBinding.hs": "1ad8ea4cec6187c7e6005651c0edeec69e196eb329fb958c833da7751225aa1b",
|
|
193
|
-
"src/LawSpec/GoProperties.hs": "
|
|
196
|
+
"src/LawSpec/GoProperties.hs": "1eee75d852b0a1f8b6fe2cfb1cd3ed7dc57f44ca0ee137697729e8e1583d85fe",
|
|
194
197
|
"src/LawSpec/GoTestHelpers.hs": "dee55ee8505370e932773b8c685f4a1f380129e6f43f8806bf1e82efb99451b1",
|
|
195
198
|
"src/LawSpec/GoTypeRefs.hs": "7f239f5ac3ac8bd21c8f76c328049f7715d8562ee1382befa0a58abd4f15f726",
|
|
196
199
|
"src/LawSpec/HaskellData.hs": "51374f35de2b1d6c78d15597e41e6f0695fe5fdb040cbb35a7d03d23fa3a3179",
|
|
197
|
-
"src/LawSpec/HaskellDefinitions.hs": "
|
|
200
|
+
"src/LawSpec/HaskellDefinitions.hs": "bc3664771247547900fc65f62147048238c3aa477154ac69d7a6e7568b05b2dd",
|
|
198
201
|
"src/LawSpec/HaskellExpr.hs": "f4144cf2022ef0a3bec6d796ebe7d2f42563e9d25758b9f362cd5545b47e802b",
|
|
199
202
|
"src/LawSpec/HaskellNativeBinding.hs": "9f98aa0c8659b572bd6486a0e29c4692bc6432db8639fa74a09bbf1b771bbfa6",
|
|
200
|
-
"src/LawSpec/HaskellProperties.hs": "
|
|
203
|
+
"src/LawSpec/HaskellProperties.hs": "5d64d5f45b86b5bb148383588d69ad717b0e158926b76938cc5cef54ed3adecc",
|
|
201
204
|
"src/LawSpec/HaskellTestHelpers.hs": "51e59b2abc9e85455e3c8e2c6baadb27e5d319081cb730d2ce56be5dc8053897",
|
|
202
205
|
"src/LawSpec/HaskellTypeRefs.hs": "2225b2a5e2ab3662e7202d23b24f4d6db48353b35ae28df8acd2b92fd2b97574",
|
|
206
|
+
"src/LawSpec/Indexed.hs": "bfb24a4023517acf9f34e6f3327c5623f0e18b1576e5453a43d7a6cd9e691e00",
|
|
203
207
|
"src/LawSpec/Inference.hs": "78285145897f03c3c4adc37acf0f490ea779bab3975fb2830628dae3a152fe38",
|
|
204
208
|
"src/LawSpec/JavaData.hs": "177713377f7a38d69fbfd7891899357fc55782cf1d361f2e9dd5baf8725d4067",
|
|
205
|
-
"src/LawSpec/JavaDefinitions.hs": "
|
|
209
|
+
"src/LawSpec/JavaDefinitions.hs": "fc2666e4f9f68b747aa596c0caf0483b9f0a47ec4957bbcd399234e77909588d",
|
|
206
210
|
"src/LawSpec/JavaExpr.hs": "6f237b0fc1eabe2d53c160f4cb781f8b77b478383f1a77ca4ccc8f08ac0dbcd0",
|
|
207
211
|
"src/LawSpec/JavaNativeBinding.hs": "8abbae78959a5cb61b7c6ae1c9071e43d10e26c2f421766d431fa18031f87173",
|
|
208
|
-
"src/LawSpec/JavaProperties.hs": "
|
|
212
|
+
"src/LawSpec/JavaProperties.hs": "d953771df667aa193b110dbc898b0936b9ff5a32f6ebc9d2ff25d44a6cd4dfec",
|
|
209
213
|
"src/LawSpec/JavaTestHelpers.hs": "e6f4b9bbb7e5f07ece6ab1845e12aa661e62e8b926680b11454223fc3ec1523d",
|
|
210
214
|
"src/LawSpec/KotlinData.hs": "7785fc0c091e50010bce3002355f01c1d237fce05bca6a7ce6de6d207737e228",
|
|
211
215
|
"src/LawSpec/KotlinDefinitions.hs": "97d7d407d4bbdb4ce16035d607dbbcb8e3d6f5db513f2123a2c269e3dc6d2b38",
|
|
212
216
|
"src/LawSpec/KotlinExpr.hs": "20312f0774f1d0832a21a19b93c8103e2791ecd32a827d8e9c4f2b4830a996a0",
|
|
213
217
|
"src/LawSpec/KotlinNativeBinding.hs": "1aec7854758f07a4d2288de13931fbb88ef32220f85d3c84fe8233ad67b6d095",
|
|
214
|
-
"src/LawSpec/KotlinProperties.hs": "
|
|
218
|
+
"src/LawSpec/KotlinProperties.hs": "ee8c68bc5ee10a601a643356edb1e0a0c50d6e72a4520feaccb716c6f02ac99c",
|
|
215
219
|
"src/LawSpec/KotlinTestHelpers.hs": "74810d65faafc6ed4d64b1fb0596cda4186e586b7144a7441641b798795bc324",
|
|
216
220
|
"src/LawSpec/Model.hs": "1d7ceb7bd66e3d496eef89eaa86403e6896472317ca9a4be86e0c933a7cce403",
|
|
217
221
|
"src/LawSpec/NativeBinding.hs": "33959b0fd432ad4150f3b685f6438e284b801c2a59d9ef3132486a1cc7c8925a",
|
|
218
222
|
"src/LawSpec/NativeRequest.hs": "fb2dc88f42424ca520533700eab68c407a5de83807d1b0d8c976e6c4f3d65212",
|
|
219
|
-
"src/LawSpec/Parser.hs": "
|
|
223
|
+
"src/LawSpec/Parser.hs": "7b11b5cbdc89a2f706623929dbdc61df84e003fac2b48b3bbafee1e2cb87a025",
|
|
220
224
|
"src/LawSpec/PortableGenerator.hs": "69d457e39f539c77fc7a4c08205e75352bbecd9dfdff031a283a39bf44b22d49",
|
|
221
225
|
"src/LawSpec/PortableTestHelpers.hs": "8ba4d87183c8c11f6ef1ce21c170127335797154f29fb97ad3429f032a167fd3",
|
|
222
226
|
"src/LawSpec/Prelude.hs": "b1e155c12f58e7c346551a0c65c5d5af76d045b3f1625977ca2a5d1c97cb8c46",
|
|
223
|
-
"src/LawSpec/Public.hs": "
|
|
227
|
+
"src/LawSpec/Public.hs": "5b986149fd3b6169e4393b8b09e67ddf6923607fb3b6af619a1c9dbd20b0280d",
|
|
224
228
|
"src/LawSpec/PythonData.hs": "07e9bb54a1515f3f4744b7b9bb43633cbdbf07b3f9e35efef511753f97e216d5",
|
|
225
|
-
"src/LawSpec/PythonDefinitions.hs": "
|
|
229
|
+
"src/LawSpec/PythonDefinitions.hs": "30f3d2d71a378e194d62261e0fd1baade5a49ac8b5eaac97578ca015a48ce8e4",
|
|
226
230
|
"src/LawSpec/PythonExpr.hs": "23d7d24b5fb4efff9cb63d38c02a93d95744ec79b4565daf1412088fc7c4ba59",
|
|
227
|
-
"src/LawSpec/PythonNativeBinding.hs": "
|
|
231
|
+
"src/LawSpec/PythonNativeBinding.hs": "4e35ff8781062d0f614e45ea45f98038c8d609ab78bd671cb0a239eaa9db1690",
|
|
228
232
|
"src/LawSpec/PythonTypes.hs": "ef928bfeb4324eb6e53c99cae247cc2fa84816075d81de4589d14abaa326656a",
|
|
229
233
|
"src/LawSpec/Refinement.hs": "8058a9f7e433b4b00ef0966a9ea2be3c91ae93b591ed58a1ffcb792d316193d3",
|
|
230
|
-
"src/LawSpec/RuntimeSources.hs": "
|
|
234
|
+
"src/LawSpec/RuntimeSources.hs": "22c9f7e4f977b4f548363f494c9d66edeb4d3247c7a1e7e7cad470b1059019be",
|
|
231
235
|
"src/LawSpec/RustData.hs": "8fda864335ccb5478c39f56cb714d5275edf36fcbdc2d152bbe94960b644ad98",
|
|
232
|
-
"src/LawSpec/RustDefinitions.hs": "
|
|
233
|
-
"src/LawSpec/RustEmit.hs": "
|
|
236
|
+
"src/LawSpec/RustDefinitions.hs": "8c289e2779d7ed623f3578f20974dd9e2016c67734ee1447ed7f4b7c22caedf9",
|
|
237
|
+
"src/LawSpec/RustEmit.hs": "0687d8589940e7b39daa953c439009d1416eba7da54e9936d2f5595316744d21",
|
|
234
238
|
"src/LawSpec/RustExpr.hs": "04b2ef81417b811c7b98a98acdadab4e588ca8757964a63ba8dc5936c36479e6",
|
|
235
239
|
"src/LawSpec/RustNativeBinding.hs": "9d08348da571ac335e335e48c1ad4a67108f7bca54119f5960480e97a6d1da84",
|
|
236
240
|
"src/LawSpec/RustNativeGenerators.hs": "a3b1484d9ff0008e4f961aa81d0aa51da57ec0cd7909124e80c98d56aacc8ce9",
|
|
237
241
|
"src/LawSpec/Scalar.hs": "52e61fec81a337bb5b35e9e6f501446cf0b18bffa6c6af2ddc5aeac2d6e07131",
|
|
238
242
|
"src/LawSpec/SpecializeDefinitions.hs": "08f2fe674a5e00d1be396d9edab0dd2c948b7beb12fa99f2a6aba00cbdf01e33",
|
|
239
|
-
"src/LawSpec/Testing.hs": "
|
|
243
|
+
"src/LawSpec/Testing.hs": "7b0a2e58bcef24365883b479b6a5af720cc2ac4c217ae23bb771c2621d82aec3",
|
|
240
244
|
"src/LawSpec/WebData.hs": "919af27be12f63f17762e0c052341ab82dcee5d45951a4fd9c66abead98d1ee0",
|
|
241
|
-
"src/LawSpec/WebDefinitions.hs": "
|
|
245
|
+
"src/LawSpec/WebDefinitions.hs": "d654df765e4bafcb1acdd35058f8a510c2cd81920153c0a75ff1e7e1137c96e5",
|
|
242
246
|
"src/LawSpec/WebExpr.hs": "0389dcd5cf7479dd23ef034b1012de6ebd70bf1dc9e3d90981dfb0ba2702596f",
|
|
243
|
-
"src/LawSpec/WebNativeBinding.hs": "
|
|
247
|
+
"src/LawSpec/WebNativeBinding.hs": "465251be1a93d24a775aa57955bb9ec612af7364c53c83017da051f44943eda9",
|
|
244
248
|
"src/LawSpec/WebTypes.hs": "f77ffee1e0c7929964a889fb252c1c5d0977afed94664ffcb0f8770c37b98c70",
|
|
245
249
|
"stack.yaml": "20ccf4d599e355e60b7aa4f814a7cd4299fe2048616cc2e6dbdc22a7bd8cec73",
|
|
246
250
|
"stack.yaml.lock": "ae222b9c81af920c56e50fa4596fa57786e7fa5a7b461390362b2b2ff63818c7",
|
|
@@ -248,8 +252,8 @@
|
|
|
248
252
|
"wasm/app/Exports.hs": "4ecbdac8faa2449e6fc61b93c82e6278fa14374154f29b4dacf0eef433f06a47",
|
|
249
253
|
"wasm/cabal.project": "021e560afdc5eb4cb7169e7119ecb8f92c9ee170245af909c94b612941bff5cc",
|
|
250
254
|
"wasm/cabal.project.freeze": "733dbed3d2ecccb26e874fd58f136296dad772184deb1658c196b5d54a0814dc",
|
|
251
|
-
"wasm/lawspec-wasm.cabal": "
|
|
252
|
-
"npm/core.wasm": "
|
|
255
|
+
"wasm/lawspec-wasm.cabal": "1d3eb3b1ab9845896d0207ecd5c2cde6ffdbf1837b79203a5eec49f62d434c34",
|
|
256
|
+
"npm/core.wasm": "7a8d98c77d07bd16dec5d8fcd2488f27eac64daaa82d4d9e1643fd93ae4130bc",
|
|
253
257
|
"npm/core_jsffi.js": "88d136efe92f7cff5758c8fec8d9b6bbc9707fe37741cebeeb415fe34ce3d72b",
|
|
254
258
|
"npm/api.mjs": "e93251db289ff0609abaff78ad67f0a9af82508c1141c6a999604f67c56b6e7d",
|
|
255
259
|
"npm/index.d.ts": "2674f8830bd20a2b9ef7dfa99e183598125b6bcc17e8d454e8f7cca1bbd2c9fe"
|
package/core.wasm
CHANGED
|
Binary file
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
unit example.indexed
|
|
2
|
+
|
|
3
|
+
-- A list indexed by its length. Each constructor states how it changes the
|
|
4
|
+
-- index; nOfVec is the checked measure that recomputes it from a value.
|
|
5
|
+
type Vec (n :: Natural) (a :: Type) is
|
|
6
|
+
| VNil where n = 0
|
|
7
|
+
| VCons head :: a tail :: Vec m a where n = m + 1
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
-- A binary tree indexed by its number of values.
|
|
11
|
+
type Tree (n :: Natural) (a :: Type) is
|
|
12
|
+
| Tip where n = 0
|
|
13
|
+
| Bin left :: Tree l a value :: a right :: Tree r a where n = l + r + 1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
-- Result indices are checked contracts over the native implementations.
|
|
17
|
+
replicate :: (n :: Natural where n < 64) -> (x :: Int8) -> (r :: Vec n Int8)
|
|
18
|
+
append :: (xs :: Vec n Int8) -> (ys :: Vec m Int8) -> (r :: Vec (n + m) Int8)
|
|
19
|
+
zip :: (xs :: Vec n Int8) -> (ys :: Vec n Bool) -> (r :: Vec n Bool)
|
|
20
|
+
flatten :: (tree :: Tree n Int8) -> (r :: Vec n Int8)
|
|
21
|
+
|
|
22
|
+
-- Checked definitions whose result indices are proved statically, by
|
|
23
|
+
-- structural induction over the constructor equations. They need no runtime
|
|
24
|
+
-- postcondition, and serve as reference models for the native adapters.
|
|
25
|
+
definition concatV (xs :: Vec n Int8) (ys :: Vec m Int8) :: Vec (n + m) Int8 is
|
|
26
|
+
match xs with
|
|
27
|
+
| VNil -> ys
|
|
28
|
+
| VCons h t -> VCons h (concatV t ys)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
definition flattenV (tree :: Tree n Int8) :: Vec n Int8 is
|
|
33
|
+
match tree with
|
|
34
|
+
| Tip -> VNil
|
|
35
|
+
| Bin l v r -> concatV (flattenV l) (VCons v (flattenV r))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
law `append agrees with the proved definition` is
|
|
40
|
+
definition is
|
|
41
|
+
`for all` (xs :: Vec n Int8) (ys :: Vec m Int8) . append xs ys = concatV xs ys
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
law `flatten agrees with the proved definition` is
|
|
46
|
+
definition is
|
|
47
|
+
`for all` (tree :: Tree 4 Int8) . flatten tree = flattenV tree
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
law `replicate has the requested length` is
|
|
52
|
+
definition is
|
|
53
|
+
`for all` (n :: Natural where n < 16) (x :: Int8) . nOfVec (replicate n x) = n
|
|
54
|
+
end
|
|
55
|
+
example `three copies` is
|
|
56
|
+
n = 3
|
|
57
|
+
x = 7
|
|
58
|
+
expect nOfVec (replicate n x) = 3
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
law `append adds lengths` is
|
|
63
|
+
definition is
|
|
64
|
+
`for all` (xs :: Vec n Int8) (ys :: Vec m Int8) .
|
|
65
|
+
nOfVec (append xs ys) = n + m
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
-- A fixed index is generated directly rather than filtered.
|
|
70
|
+
law `vectors of three elements` is
|
|
71
|
+
definition is
|
|
72
|
+
`for all` (xs :: Vec 3 Int8) . nOfVec (append xs xs) = 6
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
-- A shared index constrains the second input by the first.
|
|
77
|
+
law `zip preserves a shared length` is
|
|
78
|
+
definition is
|
|
79
|
+
`for all` (xs :: Vec n Int8) (ys :: Vec n Bool) . nOfVec (zip xs ys) = n
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
-- Generation splits the remaining index across both subtrees.
|
|
84
|
+
law `flattening five values` is
|
|
85
|
+
definition is
|
|
86
|
+
`for all` (tree :: Tree 5 Int8) . nOfVec (flatten tree) = 5
|
|
87
|
+
end
|
|
88
|
+
end
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lawspec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "State the law once. Check it everywhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"bin": { "lawspec": "bin/lawspec.mjs" },
|
|
9
9
|
"exports": { ".": { "types": "./index.d.ts", "import": "./api.mjs" } },
|
|
10
10
|
"types": "index.d.ts",
|
|
11
|
-
"files": ["*.mjs", "*.json", "index.d.ts", "bin", "core.wasm", "core_jsffi.js", "README.md", "NATIVE-BINDINGS.md", "PRIMITIVES.md", "REFINEMENTS.md", "RUST.md", "JAVA.md", "PYTHON.md", "GO.md", "HASKELL.md", "KOTLIN.md", "WEB.md", "LANGUAGE.md", "API-MIGRATION.md", "RELEASE-0.9.md", "RELEASE-0.10.md", "LICENSE", "starter.lawspec", "examples"],
|
|
11
|
+
"files": ["*.mjs", "*.json", "index.d.ts", "bin", "core.wasm", "core_jsffi.js", "README.md", "NATIVE-BINDINGS.md", "PRIMITIVES.md", "REFINEMENTS.md", "RUST.md", "JAVA.md", "PYTHON.md", "GO.md", "HASKELL.md", "KOTLIN.md", "WEB.md", "LANGUAGE.md", "API-MIGRATION.md", "RELEASE-0.9.md", "RELEASE-0.10.md", "RELEASE-0.11.md", "RELEASE-0.12.md", "LICENSE", "starter.lawspec", "examples"],
|
|
12
12
|
"scripts": { "test": "node --test test/*.test.mjs" }
|
|
13
13
|
}
|