lawspec 0.11.0 → 0.13.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 +16 -0
- package/LANGUAGE.md +132 -15
- package/NATIVE-BINDINGS.md +4 -2
- package/PRIMITIVES.md +1 -1
- package/README.md +35 -13
- package/REFINEMENTS.md +1 -1
- package/RELEASE-0.12.md +47 -0
- package/RELEASE-0.13.md +55 -0
- package/bin/lawspec.mjs +9 -3
- package/build.json +19 -15
- package/core.wasm +0 -0
- package/examples/specs/domain_modeling.lawspec +59 -0
- package/examples/specs/indexed_families.lawspec +29 -0
- package/package.json +2 -2
package/API-MIGRATION.md
CHANGED
|
@@ -278,3 +278,19 @@ 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.
|
|
290
|
+
|
|
291
|
+
## Construction evidence (0.13)
|
|
292
|
+
|
|
293
|
+
`evidence` items may have stage `construction`: a constructor field constraint,
|
|
294
|
+
including a `wrapper` constraint, checked whenever a value is constructed or
|
|
295
|
+
decoded. Its `declaration` is the constructor identity and its `status` is
|
|
296
|
+
`runtime-checked`. The TypeScript API names these records `ObligationEvidence`.
|
package/LANGUAGE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# LawSpec language and compiler boundary (0.
|
|
1
|
+
# LawSpec language and compiler boundary (0.13)
|
|
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,
|
|
@@ -182,6 +182,85 @@ shrinking stays within the index on every target. The same planning applies to
|
|
|
182
182
|
any user-written measure over declared data whose branches are a constant plus
|
|
183
183
|
the same measure of that branch's fields.
|
|
184
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
|
+
|
|
216
|
+
## Domain modeling
|
|
217
|
+
|
|
218
|
+
### Wrappers and constrained primitives
|
|
219
|
+
|
|
220
|
+
A `wrapper` gives a primitive or container a domain meaning. It declares a
|
|
221
|
+
distinct nominal type with one field, `value`, and an optional constraint:
|
|
222
|
+
|
|
223
|
+
```lawspec
|
|
224
|
+
wrapper UnitQuantity is Int32 where value >= 1 && value <= 1000 end
|
|
225
|
+
wrapper OrderId is Text where prelude.length value > 0 end
|
|
226
|
+
wrapper NonEmptyList (a :: Type) is List a where prelude.length value > 0 end
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`UnitQuantity` is not interchangeable with `Int32` or with another wrapper over
|
|
230
|
+
`Int32`. Its constructor, `UnitQuantity 5`, checks the constraint, so an invalid
|
|
231
|
+
value cannot be constructed in an example, produced by a generator, or decoded
|
|
232
|
+
from native code. `valueOfUnitQuantity` unwraps it. A wrapper elaborates to a
|
|
233
|
+
single-constructor product with a refined field and a checked definition, and
|
|
234
|
+
its native representation is that product on every target. The constraint is
|
|
235
|
+
reported as a `construction` obligation in the evidence.
|
|
236
|
+
|
|
237
|
+
### Workflows and state distinctions
|
|
238
|
+
|
|
239
|
+
A workflow names a pipeline of adapter steps between distinct state types:
|
|
240
|
+
|
|
241
|
+
```lawspec
|
|
242
|
+
type UnvalidatedOrder is UnvalidatedOrder id :: Text quantity :: Int32 end
|
|
243
|
+
type ValidatedOrder is ValidatedOrder id :: OrderId quantity :: UnitQuantity end
|
|
244
|
+
type PricedOrder is PricedOrder id :: OrderId quantity :: UnitQuantity total :: Int64 end
|
|
245
|
+
|
|
246
|
+
workflow placeOrder :: UnvalidatedOrder -> Either OrderError PricedOrder is
|
|
247
|
+
validateOrder :: UnvalidatedOrder -> Either OrderError ValidatedOrder
|
|
248
|
+
priceOrder :: ValidatedOrder -> Either OrderError PricedOrder
|
|
249
|
+
end
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Each step is an ordinary adapter declaration with one input. The compiler checks
|
|
253
|
+
that every step accepts the state the previous step produces, that fallible steps
|
|
254
|
+
(those returning `Either E T`) share one error type, and that the workflow's
|
|
255
|
+
declared result matches: `Either E T` if any step can fail, otherwise `T`. It then
|
|
256
|
+
adds the law `placeOrder composes its steps`: the native workflow must equal the
|
|
257
|
+
railway composition of the native steps, where the first `Left` is the result. A
|
|
258
|
+
step declared again with the same type is shared between workflows.
|
|
259
|
+
|
|
260
|
+
Because each state is its own type, a `ValidatedOrder` can only hold checked
|
|
261
|
+
values, and a step that needs one cannot be given raw input. See the
|
|
262
|
+
[domain modeling example](examples/specs/domain_modeling.lawspec).
|
|
263
|
+
|
|
185
264
|
## Total definitions
|
|
186
265
|
|
|
187
266
|
A unit can supply an implementation as a checked total definition:
|
|
@@ -385,20 +464,58 @@ API schema v3 uses separately defined wire views, with lossless tagged scalar
|
|
|
385
464
|
values. It does not serialize internal AST constructors. See the
|
|
386
465
|
[API migration guide](API-MIGRATION.md).
|
|
387
466
|
|
|
388
|
-
##
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
0.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
467
|
+
## Roadmap
|
|
468
|
+
|
|
469
|
+
0.11 elaborates natural-indexed families to erased data, measures and
|
|
470
|
+
refinements. The Core type model distinguishes type arguments from index
|
|
471
|
+
arguments, but that representation is not a claim that arbitrary dependent
|
|
472
|
+
programs are accepted. External type bindings and custom generator bindings
|
|
473
|
+
(0.10) configure native representations alongside the typed testing plan and do
|
|
474
|
+
not change source-language typing or equality; see
|
|
475
|
+
[the binding reference](NATIVE-BINDINGS.md).
|
|
476
|
+
|
|
477
|
+
### 0.12 Proof-producing dependent layer (released)
|
|
478
|
+
|
|
479
|
+
- Index equalities in checked definitions are discharged statically. Adapter
|
|
480
|
+
results remain runtime-checked.
|
|
481
|
+
- Linear `Natural` arithmetic over measures is solved exactly, with unfolding
|
|
482
|
+
on known constructors and induction through recursive calls.
|
|
483
|
+
- Evidence is recorded for every contract obligation as `proved` or
|
|
484
|
+
`runtime-checked`, and reported by `lawspec check` and the API.
|
|
485
|
+
- Proved definition postconditions produce no runtime checks.
|
|
486
|
+
- Checked definitions may return indexed families. See
|
|
487
|
+
[proved indices](#proved-indices-and-evidence).
|
|
488
|
+
|
|
489
|
+
Still open, for evaluation in later releases: index equalities between sibling
|
|
490
|
+
fields (perfect trees whose subtrees share one index), non-linear indices, and
|
|
491
|
+
GADTs that refine type arguments.
|
|
492
|
+
|
|
493
|
+
### 0.13 Wlaschin-style domain modeling primitives (released)
|
|
494
|
+
|
|
495
|
+
- Semantic wrappers and constrained primitives: `wrapper` declarations with
|
|
496
|
+
checked constructors.
|
|
497
|
+
- Illegal states are unrepresentable: wrapper and field constraints are checked
|
|
498
|
+
on construction, generation and native decoding, and reported as evidence.
|
|
499
|
+
- Explicit domain workflows and state distinctions: `workflow` declarations with
|
|
500
|
+
checked step types and a generated railway composition law.
|
|
501
|
+
- See [domain modeling](#domain-modeling).
|
|
502
|
+
|
|
503
|
+
### 0.14 Cross-unit imports and packages
|
|
504
|
+
|
|
505
|
+
- Reusable law, type and refinement libraries.
|
|
506
|
+
- Versioning and namespacing, including constructor names scoped to their unit.
|
|
507
|
+
Today every unit compiled together needs distinct constructor names.
|
|
508
|
+
- Publishable behavioral contracts.
|
|
509
|
+
|
|
510
|
+
### 0.15 Evidence/discharge model
|
|
511
|
+
|
|
512
|
+
Each obligation reports how it was discharged:
|
|
513
|
+
|
|
514
|
+
- `PROVED`: discharged statically, by 0.12 evidence or definition proofs.
|
|
515
|
+
- `EXHAUSTIVELY CHECKED`: every value of a finite domain was checked.
|
|
516
|
+
- `PROPERTY TESTED`: generated cases, examples and boundaries.
|
|
517
|
+
- `RUNTIME CHECKED`: adapter contracts and checked codecs at native boundaries.
|
|
518
|
+
- `ASSUMED / EXTERNAL`: native adapters and bindings taken on trust.
|
|
402
519
|
|
|
403
520
|
## Generated project formatting
|
|
404
521
|
|
package/NATIVE-BINDINGS.md
CHANGED
|
@@ -1045,5 +1045,7 @@ after the empty-parameter changes.
|
|
|
1045
1045
|
regeneration protection and packaged installation remain tested.
|
|
1046
1046
|
- Python follows PEP 8; Rust remains a first-class acceptance target throughout.
|
|
1047
1047
|
|
|
1048
|
-
|
|
1049
|
-
|
|
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.13 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
|
|
|
@@ -60,6 +60,28 @@ index instead of filtering for it. See the
|
|
|
60
60
|
[indexed example](examples/specs/indexed_families.lawspec), and the
|
|
61
61
|
[release notes](RELEASE-0.11.md).
|
|
62
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
|
+
## Domain modeling in 0.13
|
|
73
|
+
|
|
74
|
+
LawSpec 0.13 adds `wrapper` declarations for constrained primitives, such as a
|
|
75
|
+
`UnitQuantity` that cannot hold 0, and `workflow` declarations for typed
|
|
76
|
+
pipelines between domain states, with a generated law that the workflow is the
|
|
77
|
+
railway composition of its steps. See
|
|
78
|
+
[domain modeling](LANGUAGE.md#domain-modeling), the
|
|
79
|
+
[example](examples/specs/domain_modeling.lawspec) and the
|
|
80
|
+
[release notes](RELEASE-0.13.md).
|
|
81
|
+
|
|
82
|
+
Planned: 0.14 cross-unit packages and 0.15 evidence reporting. See the
|
|
83
|
+
[roadmap](LANGUAGE.md#roadmap).
|
|
84
|
+
|
|
63
85
|
## Install and try it
|
|
64
86
|
|
|
65
87
|
LawSpec has its own [syntax-highlighting grammar and VS Code extension](editors/vscode/README.md)
|
|
@@ -69,7 +91,7 @@ support to use it; GitHub currently uses the Haskell fallback.
|
|
|
69
91
|
Install [LawSpec from npm](https://www.npmjs.com/package/lawspec):
|
|
70
92
|
|
|
71
93
|
```sh
|
|
72
|
-
npm install --save-dev lawspec@0.
|
|
94
|
+
npm install --save-dev lawspec@0.13.0
|
|
73
95
|
npx lawspec --version
|
|
74
96
|
```
|
|
75
97
|
|
|
@@ -83,8 +105,8 @@ local dependencies so LawSpec can create its `package.json` and test script:
|
|
|
83
105
|
```sh
|
|
84
106
|
mkdir lawspec-example
|
|
85
107
|
cd lawspec-example
|
|
86
|
-
npm exec --package=lawspec@0.
|
|
87
|
-
npm install --save-dev lawspec@0.
|
|
108
|
+
npm exec --package=lawspec@0.13.0 -- lawspec init --target javascript
|
|
109
|
+
npm install --save-dev lawspec@0.13.0
|
|
88
110
|
npx lawspec check
|
|
89
111
|
npx lawspec explain 'example.atoi_codec::itoa and then atoi yields a'
|
|
90
112
|
npx lawspec doctor
|
|
@@ -294,7 +316,7 @@ reusable laws accept these curried functions, their partial applications, and
|
|
|
294
316
|
scalar parameters. Quantified test inputs remain scalar.
|
|
295
317
|
|
|
296
318
|
The prelude defines the following laws. Every row has an executable example in
|
|
297
|
-
[algebra.lawspec](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
319
|
+
[algebra.lawspec](https://github.com/brain-fuel/lawspec/blob/v0.13.0/examples/specs/algebra.lawspec), including both sides of every
|
|
298
320
|
combined law. `f` and `g` are binary operations, `inverse` is unary, and `e` and
|
|
299
321
|
`zero` are scalar parameters. All these laws require equality of the element type.
|
|
300
322
|
|
|
@@ -362,7 +384,7 @@ their entire consequence. Every conjunct is type-checked and emitted. As with
|
|
|
362
384
|
existing assertions, the first failure stops that individual test. `and` is now
|
|
363
385
|
a reserved word.
|
|
364
386
|
|
|
365
|
-
[Currying examples](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
387
|
+
[Currying examples](https://github.com/brain-fuel/lawspec/blob/v0.13.0/examples/specs/currying.lawspec) demonstrate a four-argument
|
|
366
388
|
function partially applied twice, a formatter with four heterogeneous arguments,
|
|
367
389
|
and composition after partial application. Each example states its exact outputs.
|
|
368
390
|
Run `node npm/bin/lawspec.mjs examples` after rebuilding to inspect all nine units
|
|
@@ -414,7 +436,7 @@ law `valid ports round trip` is
|
|
|
414
436
|
end
|
|
415
437
|
```
|
|
416
438
|
|
|
417
|
-
The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
439
|
+
The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.13.0/examples/specs/parse_port.lawspec)
|
|
418
440
|
defines valid ports as 1–65535, and covers both endpoints, ordinary ports, zero,
|
|
419
441
|
negative values, and 65536. All explicit `expect` assertions run regardless of
|
|
420
442
|
the law's condition. A false condition skips only the consequence: invalid ports
|
|
@@ -432,7 +454,7 @@ The prelude includes `satisfies predicate` (the predicate holds for every input)
|
|
|
432
454
|
and `left inverse when predicate parse render` (the guarded round trip above).
|
|
433
455
|
These reusable laws preserve the condition and its lexical bindings when expanded.
|
|
434
456
|
`equivalent` can also compare two predicates, since `Bool` supports equality.
|
|
435
|
-
The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
457
|
+
The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.13.0/examples/specs/boolean_flags.lawspec)
|
|
436
458
|
checks that flipping twice restores both `false` and `true`; all targets generate
|
|
437
459
|
Boolean property inputs and explicit tests for both Boolean boundary values. Java caps Boolean-only JetCheck runs at the number of
|
|
438
460
|
possible input combinations (up to 100), avoiding generator exhaustion.
|
|
@@ -511,7 +533,7 @@ This expands to `for all (x :: Int32) . render (x) = referenceRender (x)`.
|
|
|
511
533
|
The example inherits the input name `x` from the prelude. Both functions are
|
|
512
534
|
user-owned adapter functions; either may delegate to your existing code.
|
|
513
535
|
|
|
514
|
-
[The complete example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
536
|
+
[The complete example](https://github.com/brain-fuel/lawspec/blob/v0.13.0/examples/specs/equivalent.lawspec) compares decimal
|
|
515
537
|
renderers and two implementations that clamp negative integers to zero. For
|
|
516
538
|
JavaScript, their adapters can be:
|
|
517
539
|
|
|
@@ -548,7 +570,7 @@ law `normalizers agree` is
|
|
|
548
570
|
end
|
|
549
571
|
```
|
|
550
572
|
|
|
551
|
-
The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
573
|
+
The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.13.0/examples/specs/slug.lawspec)
|
|
552
574
|
compares two implementations of ASCII-space replacement. It includes empty,
|
|
553
575
|
Unicode and escaped text. Each target uses its native string generator:
|
|
554
576
|
JetCheck `Generator.stringsOf(Generator.asciiPrintableChars())`, Hypothesis `st.text()`, fast-check `fc.string()`,
|
|
@@ -575,7 +597,7 @@ law `canonicalization reaches a fixed point` is
|
|
|
575
597
|
end
|
|
576
598
|
```
|
|
577
599
|
|
|
578
|
-
The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
600
|
+
The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.13.0/examples/specs/canonical_url.lawspec)
|
|
579
601
|
uses removal of **all trailing slashes** as a small fixed-point demonstration,
|
|
580
602
|
not a complete URL canonicalization algorithm. For JavaScript:
|
|
581
603
|
|
|
@@ -584,7 +606,7 @@ export const canonicalize = value => value.replace(/\/+$/, "");
|
|
|
584
606
|
```
|
|
585
607
|
|
|
586
608
|
Removing just one trailing slash fails the supplied repeated-slash example.
|
|
587
|
-
The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
609
|
+
The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.13.0/examples/specs/mixed_inputs.lawspec)
|
|
588
610
|
shows `Text` and `Int32` in the same quantified property and executable example.
|
|
589
611
|
The JavaScript API represents example bindings as typed records whose values use
|
|
590
612
|
`DataValue`: lossless tagged scalar payloads or structural constructors. Expected results are
|
|
@@ -667,7 +689,7 @@ by the JS shim.
|
|
|
667
689
|
## Build and verify
|
|
668
690
|
|
|
669
691
|
For contributors working from a repository checkout, build a local archive with
|
|
670
|
-
`npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.
|
|
692
|
+
`npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.13.0.tgz`.
|
|
671
693
|
The package payload lives in `npm/`.
|
|
672
694
|
|
|
673
695
|
```sh
|
package/REFINEMENTS.md
CHANGED
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/RELEASE-0.13.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# LawSpec 0.13.0
|
|
2
|
+
|
|
3
|
+
## Wrappers and constrained primitives
|
|
4
|
+
|
|
5
|
+
```lawspec
|
|
6
|
+
wrapper UnitQuantity is Int32 where value >= 1 && value <= 1000 end
|
|
7
|
+
wrapper NonEmptyList (a :: Type) is List a where prelude.length value > 0 end
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
A wrapper declares a distinct nominal type with one field, `value`, and an
|
|
11
|
+
optional constraint. Its constructor checks the constraint, so an invalid value
|
|
12
|
+
cannot be constructed in an example (the compiler rejects `UnitQuantity 0`),
|
|
13
|
+
produced by a generator, or decoded from native code. `valueOf<Name>` unwraps a
|
|
14
|
+
value. Wrappers may take type parameters. They elaborate to a single-constructor
|
|
15
|
+
product with a refined field, so all eight targets represent them natively with
|
|
16
|
+
no new runtime machinery.
|
|
17
|
+
|
|
18
|
+
## Workflows and state distinctions
|
|
19
|
+
|
|
20
|
+
```lawspec
|
|
21
|
+
workflow placeOrder :: UnvalidatedOrder -> Either OrderError PricedOrder is
|
|
22
|
+
validateOrder :: UnvalidatedOrder -> Either OrderError ValidatedOrder
|
|
23
|
+
priceOrder :: ValidatedOrder -> Either OrderError PricedOrder
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A workflow declares its steps as adapters and checks the pipeline: each step
|
|
28
|
+
must accept the previous step's state, and fallible steps must share an error
|
|
29
|
+
type. The workflow's result must match `Either E T` when any step can fail, or
|
|
30
|
+
`T` otherwise. The compiler adds the law `placeOrder composes its steps`: the
|
|
31
|
+
native workflow must equal the railway composition of the native steps.
|
|
32
|
+
Diagnostics name the step and the mismatched types, with the workflow's source
|
|
33
|
+
location.
|
|
34
|
+
|
|
35
|
+
## Evidence
|
|
36
|
+
|
|
37
|
+
Constructor field constraints, including wrapper constraints, appear in the
|
|
38
|
+
evidence as `construction` obligations with status `runtime-checked`, next to
|
|
39
|
+
the contract obligations introduced in 0.12. The TypeScript API declares these
|
|
40
|
+
records as `ObligationEvidence`; the 0.12 typing was not published.
|
|
41
|
+
|
|
42
|
+
## Examples and acceptance
|
|
43
|
+
|
|
44
|
+
`examples/specs/domain_modeling.lawspec` combines wrappers, a parameterized
|
|
45
|
+
`NonEmptyList`, order states and the workflow. `tools/domain-integration.mjs`
|
|
46
|
+
runs it on all eight targets in both machine profiles. Correct adapters pass;
|
|
47
|
+
mutants that break a wrapper invariant, the railway composition, or a non-empty
|
|
48
|
+
list operation fail. The example runners now share
|
|
49
|
+
`tools/example-acceptance.mjs`.
|
|
50
|
+
|
|
51
|
+
## Compatibility
|
|
52
|
+
|
|
53
|
+
`wrapper` and `workflow` begin new declarations. Specifications that do not use
|
|
54
|
+
them compile as in 0.12. API schemas 3 and 4 are unchanged, apart from the
|
|
55
|
+
additive `construction` stage in `evidence`.
|
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.13.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.13.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",
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
"src/LawSpec/Data.hs",
|
|
57
58
|
"src/LawSpec/DefinitionTotality.hs",
|
|
58
59
|
"src/LawSpec/Domain.hs",
|
|
60
|
+
"src/LawSpec/DomainModel.hs",
|
|
59
61
|
"src/LawSpec/Elaboration.hs",
|
|
60
62
|
"src/LawSpec/Emit.hs",
|
|
61
63
|
"src/LawSpec/Eval.hs",
|
|
@@ -127,8 +129,8 @@
|
|
|
127
129
|
"wasm/lawspec-wasm.cabal"
|
|
128
130
|
],
|
|
129
131
|
"digests": {
|
|
130
|
-
"lawspec.cabal": "
|
|
131
|
-
"package.yaml": "
|
|
132
|
+
"lawspec.cabal": "2aab726a6d4e9c64e218c0563d52fab738b0753f9597683b9d650dde1baa948c",
|
|
133
|
+
"package.yaml": "95807129285b33f8ac2bf7452aae1ee049f8f7711f0dc9f962fdf9586b58fcc8",
|
|
132
134
|
"runtime/LawSpecCodecs.hs": "90cb1ed15d7ab664a17a054580c849408d77cfd2b2adcb4a3228b05132caeaca",
|
|
133
135
|
"runtime/LawSpecDataStrategies.hs": "cbe9f1946228db1076c19cd3158e8798fe32766cfdbea3f66b63d22d3d217508",
|
|
134
136
|
"runtime/LawSpecDataStrategies.java": "e3416efe8a0d7b1276cb4d1a8105fd01b1f5f3f02a803bf55d955d9e3ec39095",
|
|
@@ -152,8 +154,8 @@
|
|
|
152
154
|
"runtime/lawspec_schema.mjs": "60eb4204461eaa2c207fc7c3394014e2a814e4383b7045c065b4dae7c7a202c2",
|
|
153
155
|
"runtime/lawspec_schema.py": "b6967ef1a33fe6a1a180d5573ec37cd8bb2079c06e961ac2eeb05f23cffcdd07",
|
|
154
156
|
"runtime/lawspec_strategies.rs": "cac9ca301d4614fff9acfe61e7cc5678fabc044edadfa89ec11599b6780dc5f3",
|
|
155
|
-
"runtime/rust/Cargo.lock": "
|
|
156
|
-
"runtime/rust/Cargo.toml": "
|
|
157
|
+
"runtime/rust/Cargo.lock": "deb6b268124278aa237f64c8e821caf2388ccda3df92c2e814a5863331ae8c92",
|
|
158
|
+
"runtime/rust/Cargo.toml": "ed36668f25943b9c8a3ea9eb5a805f2953540aabb763919029947d342cc23978",
|
|
157
159
|
"src/LawSpec/Api.hs": "392f6e8c1a9ae81a95e219ac071e5b4bea03ee330d63b829036f8f54b2eda8a7",
|
|
158
160
|
"src/LawSpec/Backend.hs": "03c3b0cd62467ca677c88d29d7b12b2ab505cc0076d93d1f48fbddb0e9bcf133",
|
|
159
161
|
"src/LawSpec/Capabilities.hs": "e43d4fc3a4481fbb8aa3ff1c3f41f7e7fca5db01bfe256a30162a45d75a00c1c",
|
|
@@ -165,6 +167,7 @@
|
|
|
165
167
|
"src/LawSpec/Core/DefinitionContracts.hs": "b7f60f038b231187ecf898e585ae1a8fb1fc113bb55bad6d1bb2660350fd969e",
|
|
166
168
|
"src/LawSpec/Core/Definitions.hs": "52f024fca59661142f70f9aa7dd2cbdb4892e5d3b1ca8079c878c5a1ba667112",
|
|
167
169
|
"src/LawSpec/Core/Eval.hs": "e1ad8af5b240dd99c3b1126408d2be5f9b42b86ca6189ecd3868680a5119d931",
|
|
170
|
+
"src/LawSpec/Core/Evidence.hs": "460af282299258c87ddba58b55ad09290d7d89e6e7d43a02f5470fbd5b7974a3",
|
|
168
171
|
"src/LawSpec/Core/Expression.hs": "84985f4f685126253d3af7b6b605d80d28fc9aa115bd5e2946f989eeb1355224",
|
|
169
172
|
"src/LawSpec/Core/Payload.hs": "df6a2b250c27c12bb6ac16b84847c52546081d066a3dca7575ed0d743cc13d50",
|
|
170
173
|
"src/LawSpec/Core/PayloadPlan.hs": "f2a7d2dc58c6a0f79707bdc24773bd3aad3a3b8eea2735a8284b2a9f3a4af4a8",
|
|
@@ -172,7 +175,7 @@
|
|
|
172
175
|
"src/LawSpec/Core/Schema.hs": "9a4ec3967803913e6ff43d33e00a1b4b3b8ec0c79dbdbd80ae98101c85bf430f",
|
|
173
176
|
"src/LawSpec/Core/Semantics.hs": "a9d019433e3b9731f8cd0f65ec0ccc53179824bea58247b72df26760b878c2b6",
|
|
174
177
|
"src/LawSpec/Core/Total.hs": "95d2b19ba7fbfd033295504d31f786815384003b3b4d370776860b423cf079dd",
|
|
175
|
-
"src/LawSpec/Core/Totality.hs": "
|
|
178
|
+
"src/LawSpec/Core/Totality.hs": "a96fc3b9e8be5d18997065b9d09c0f8a2ede4d6ed2974b3bf355967986b0e459",
|
|
176
179
|
"src/LawSpec/Core/Types.hs": "48ed1d197ce7089737895b7279f93437301fcfacff8b3a8dd98ecd7f1cb32b37",
|
|
177
180
|
"src/LawSpec/Core/Validate.hs": "0f045241215e068081d6cd7cf451f0166671bae5fffafda71a2e2320e8014c27",
|
|
178
181
|
"src/LawSpec/Core/Value.hs": "f2b2e4d82491e184d60b1217922b25c57a06cbbaecb2cb6f31174af50268fbc9",
|
|
@@ -182,20 +185,21 @@
|
|
|
182
185
|
"src/LawSpec/Data.hs": "9dbeafa120276b114dad790ec69e617f7ad0c632e3c718962f2f7faf8ca327f4",
|
|
183
186
|
"src/LawSpec/DefinitionTotality.hs": "7671b7efb0cac5d796b074d48b2c10940e5bd0b5df082b926aee694903285efa",
|
|
184
187
|
"src/LawSpec/Domain.hs": "222d15f0012ae630802238a6d7e1ee78d1e718b599f5ebb5a1ddc4e8d29d68c3",
|
|
188
|
+
"src/LawSpec/DomainModel.hs": "3be67117c44e66a39d42d7ce094c2940c48ea1541017b64027afa6ea4b77092f",
|
|
185
189
|
"src/LawSpec/Elaboration.hs": "86e3ef77a977cb1b55985e2939648135acd0c2c015afe76e34e09615d555f53e",
|
|
186
190
|
"src/LawSpec/Emit.hs": "9f34f5765915a2a12a66a6dca647ddaef9289372956802c6d99cf0cfec447c7a",
|
|
187
191
|
"src/LawSpec/Eval.hs": "b0872ba8a6974463b32776653c2939826a6be87318c15641401f913f6b139de2",
|
|
188
192
|
"src/LawSpec/Frontend.hs": "fbf091b9c96f5b5361dbf4f65819634c0f8526ff98550d97c434c0d47af7839e",
|
|
189
193
|
"src/LawSpec/Gen.hs": "c76d00fb1f7e9ed2eed7d387f1cd86cf4d12cc57f007abb0e479043b0ed377e6",
|
|
190
194
|
"src/LawSpec/GoData.hs": "35efbfdc1079e777d4760069fa2405a9700bbabab7dc7e3c2c5e0757312ea201",
|
|
191
|
-
"src/LawSpec/GoDefinitions.hs": "
|
|
195
|
+
"src/LawSpec/GoDefinitions.hs": "3cb55c22f28443fff73e73c59780bf1977ab9260b8d66c7f19d1b94b40d1397e",
|
|
192
196
|
"src/LawSpec/GoExpr.hs": "da3a7cc0e3d2906886dc44f6b990e72098bb351558bbb31ee335da7382fac58a",
|
|
193
197
|
"src/LawSpec/GoNativeBinding.hs": "1ad8ea4cec6187c7e6005651c0edeec69e196eb329fb958c833da7751225aa1b",
|
|
194
198
|
"src/LawSpec/GoProperties.hs": "1eee75d852b0a1f8b6fe2cfb1cd3ed7dc57f44ca0ee137697729e8e1583d85fe",
|
|
195
199
|
"src/LawSpec/GoTestHelpers.hs": "dee55ee8505370e932773b8c685f4a1f380129e6f43f8806bf1e82efb99451b1",
|
|
196
200
|
"src/LawSpec/GoTypeRefs.hs": "7f239f5ac3ac8bd21c8f76c328049f7715d8562ee1382befa0a58abd4f15f726",
|
|
197
201
|
"src/LawSpec/HaskellData.hs": "51374f35de2b1d6c78d15597e41e6f0695fe5fdb040cbb35a7d03d23fa3a3179",
|
|
198
|
-
"src/LawSpec/HaskellDefinitions.hs": "
|
|
202
|
+
"src/LawSpec/HaskellDefinitions.hs": "bc3664771247547900fc65f62147048238c3aa477154ac69d7a6e7568b05b2dd",
|
|
199
203
|
"src/LawSpec/HaskellExpr.hs": "f4144cf2022ef0a3bec6d796ebe7d2f42563e9d25758b9f362cd5545b47e802b",
|
|
200
204
|
"src/LawSpec/HaskellNativeBinding.hs": "9f98aa0c8659b572bd6486a0e29c4692bc6432db8639fa74a09bbf1b771bbfa6",
|
|
201
205
|
"src/LawSpec/HaskellProperties.hs": "5d64d5f45b86b5bb148383588d69ad717b0e158926b76938cc5cef54ed3adecc",
|
|
@@ -204,7 +208,7 @@
|
|
|
204
208
|
"src/LawSpec/Indexed.hs": "bfb24a4023517acf9f34e6f3327c5623f0e18b1576e5453a43d7a6cd9e691e00",
|
|
205
209
|
"src/LawSpec/Inference.hs": "78285145897f03c3c4adc37acf0f490ea779bab3975fb2830628dae3a152fe38",
|
|
206
210
|
"src/LawSpec/JavaData.hs": "177713377f7a38d69fbfd7891899357fc55782cf1d361f2e9dd5baf8725d4067",
|
|
207
|
-
"src/LawSpec/JavaDefinitions.hs": "
|
|
211
|
+
"src/LawSpec/JavaDefinitions.hs": "fc2666e4f9f68b747aa596c0caf0483b9f0a47ec4957bbcd399234e77909588d",
|
|
208
212
|
"src/LawSpec/JavaExpr.hs": "6f237b0fc1eabe2d53c160f4cb781f8b77b478383f1a77ca4ccc8f08ac0dbcd0",
|
|
209
213
|
"src/LawSpec/JavaNativeBinding.hs": "8abbae78959a5cb61b7c6ae1c9071e43d10e26c2f421766d431fa18031f87173",
|
|
210
214
|
"src/LawSpec/JavaProperties.hs": "d953771df667aa193b110dbc898b0936b9ff5a32f6ebc9d2ff25d44a6cd4dfec",
|
|
@@ -218,20 +222,20 @@
|
|
|
218
222
|
"src/LawSpec/Model.hs": "1d7ceb7bd66e3d496eef89eaa86403e6896472317ca9a4be86e0c933a7cce403",
|
|
219
223
|
"src/LawSpec/NativeBinding.hs": "33959b0fd432ad4150f3b685f6438e284b801c2a59d9ef3132486a1cc7c8925a",
|
|
220
224
|
"src/LawSpec/NativeRequest.hs": "fb2dc88f42424ca520533700eab68c407a5de83807d1b0d8c976e6c4f3d65212",
|
|
221
|
-
"src/LawSpec/Parser.hs": "
|
|
225
|
+
"src/LawSpec/Parser.hs": "94a5603f6831d1dad5634a766bbc02cc58d884d23b5d6606ed4a1537cfe4c813",
|
|
222
226
|
"src/LawSpec/PortableGenerator.hs": "69d457e39f539c77fc7a4c08205e75352bbecd9dfdff031a283a39bf44b22d49",
|
|
223
227
|
"src/LawSpec/PortableTestHelpers.hs": "8ba4d87183c8c11f6ef1ce21c170127335797154f29fb97ad3429f032a167fd3",
|
|
224
228
|
"src/LawSpec/Prelude.hs": "b1e155c12f58e7c346551a0c65c5d5af76d045b3f1625977ca2a5d1c97cb8c46",
|
|
225
|
-
"src/LawSpec/Public.hs": "
|
|
229
|
+
"src/LawSpec/Public.hs": "ca7f3edd3e5d623ecb1aecb2386fdcf4b1e334cf7bd7770b6a14f5690d8f5ed5",
|
|
226
230
|
"src/LawSpec/PythonData.hs": "07e9bb54a1515f3f4744b7b9bb43633cbdbf07b3f9e35efef511753f97e216d5",
|
|
227
|
-
"src/LawSpec/PythonDefinitions.hs": "
|
|
231
|
+
"src/LawSpec/PythonDefinitions.hs": "30f3d2d71a378e194d62261e0fd1baade5a49ac8b5eaac97578ca015a48ce8e4",
|
|
228
232
|
"src/LawSpec/PythonExpr.hs": "23d7d24b5fb4efff9cb63d38c02a93d95744ec79b4565daf1412088fc7c4ba59",
|
|
229
233
|
"src/LawSpec/PythonNativeBinding.hs": "4e35ff8781062d0f614e45ea45f98038c8d609ab78bd671cb0a239eaa9db1690",
|
|
230
234
|
"src/LawSpec/PythonTypes.hs": "ef928bfeb4324eb6e53c99cae247cc2fa84816075d81de4589d14abaa326656a",
|
|
231
235
|
"src/LawSpec/Refinement.hs": "8058a9f7e433b4b00ef0966a9ea2be3c91ae93b591ed58a1ffcb792d316193d3",
|
|
232
236
|
"src/LawSpec/RuntimeSources.hs": "22c9f7e4f977b4f548363f494c9d66edeb4d3247c7a1e7e7cad470b1059019be",
|
|
233
237
|
"src/LawSpec/RustData.hs": "8fda864335ccb5478c39f56cb714d5275edf36fcbdc2d152bbe94960b644ad98",
|
|
234
|
-
"src/LawSpec/RustDefinitions.hs": "
|
|
238
|
+
"src/LawSpec/RustDefinitions.hs": "8c289e2779d7ed623f3578f20974dd9e2016c67734ee1447ed7f4b7c22caedf9",
|
|
235
239
|
"src/LawSpec/RustEmit.hs": "0687d8589940e7b39daa953c439009d1416eba7da54e9936d2f5595316744d21",
|
|
236
240
|
"src/LawSpec/RustExpr.hs": "04b2ef81417b811c7b98a98acdadab4e588ca8757964a63ba8dc5936c36479e6",
|
|
237
241
|
"src/LawSpec/RustNativeBinding.hs": "9d08348da571ac335e335e48c1ad4a67108f7bca54119f5960480e97a6d1da84",
|
|
@@ -240,7 +244,7 @@
|
|
|
240
244
|
"src/LawSpec/SpecializeDefinitions.hs": "08f2fe674a5e00d1be396d9edab0dd2c948b7beb12fa99f2a6aba00cbdf01e33",
|
|
241
245
|
"src/LawSpec/Testing.hs": "7b0a2e58bcef24365883b479b6a5af720cc2ac4c217ae23bb771c2621d82aec3",
|
|
242
246
|
"src/LawSpec/WebData.hs": "919af27be12f63f17762e0c052341ab82dcee5d45951a4fd9c66abead98d1ee0",
|
|
243
|
-
"src/LawSpec/WebDefinitions.hs": "
|
|
247
|
+
"src/LawSpec/WebDefinitions.hs": "d654df765e4bafcb1acdd35058f8a510c2cd81920153c0a75ff1e7e1137c96e5",
|
|
244
248
|
"src/LawSpec/WebExpr.hs": "0389dcd5cf7479dd23ef034b1012de6ebd70bf1dc9e3d90981dfb0ba2702596f",
|
|
245
249
|
"src/LawSpec/WebNativeBinding.hs": "465251be1a93d24a775aa57955bb9ec612af7364c53c83017da051f44943eda9",
|
|
246
250
|
"src/LawSpec/WebTypes.hs": "f77ffee1e0c7929964a889fb252c1c5d0977afed94664ffcb0f8770c37b98c70",
|
|
@@ -250,8 +254,8 @@
|
|
|
250
254
|
"wasm/app/Exports.hs": "4ecbdac8faa2449e6fc61b93c82e6278fa14374154f29b4dacf0eef433f06a47",
|
|
251
255
|
"wasm/cabal.project": "021e560afdc5eb4cb7169e7119ecb8f92c9ee170245af909c94b612941bff5cc",
|
|
252
256
|
"wasm/cabal.project.freeze": "733dbed3d2ecccb26e874fd58f136296dad772184deb1658c196b5d54a0814dc",
|
|
253
|
-
"wasm/lawspec-wasm.cabal": "
|
|
254
|
-
"npm/core.wasm": "
|
|
257
|
+
"wasm/lawspec-wasm.cabal": "ee8235aa57e891fe5b1f0d798694f5db5bc6e83b4d013fd1a96e9b2d28aae613",
|
|
258
|
+
"npm/core.wasm": "f67a99e2de93750f0d4930cc086688a74ee1d309c3818bfe3125490e01b08660",
|
|
255
259
|
"npm/core_jsffi.js": "88d136efe92f7cff5758c8fec8d9b6bbc9707fe37741cebeeb415fe34ce3d72b",
|
|
256
260
|
"npm/api.mjs": "e93251db289ff0609abaff78ad67f0a9af82508c1141c6a999604f67c56b6e7d",
|
|
257
261
|
"npm/index.d.ts": "2674f8830bd20a2b9ef7dfa99e183598125b6bcc17e8d454e8f7cca1bbd2c9fe"
|
package/core.wasm
CHANGED
|
Binary file
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
unit example.ordering
|
|
2
|
+
|
|
3
|
+
-- Constrained primitives. Each wrapper is a distinct type whose constructor
|
|
4
|
+
-- checks the value, so an out-of-range quantity cannot be constructed,
|
|
5
|
+
-- generated or decoded from native code. valueOf<Name> unwraps a value.
|
|
6
|
+
wrapper UnitQuantity is Int32 where value >= 1 && value <= 1000 end
|
|
7
|
+
wrapper OrderId is Text where prelude.length value > 0 end
|
|
8
|
+
wrapper NonEmptyList (a :: Type) is List a where prelude.length value > 0 end
|
|
9
|
+
|
|
10
|
+
-- Each workflow state is its own type. A ValidatedOrder can only hold checked
|
|
11
|
+
-- values, so no later step needs to validate again.
|
|
12
|
+
type UnvalidatedOrder is UnvalidatedOrder id :: Text quantity :: Int32 end
|
|
13
|
+
type ValidatedOrder is ValidatedOrder id :: OrderId quantity :: UnitQuantity end
|
|
14
|
+
type PricedOrder is PricedOrder id :: OrderId quantity :: UnitQuantity total :: Int64 end
|
|
15
|
+
type OrderError is | InvalidOrderId | InvalidQuantity | PriceTooHigh end
|
|
16
|
+
|
|
17
|
+
-- The compiler checks that each step accepts the previous step's state and that
|
|
18
|
+
-- all failures share one error type. It adds a law that placeOrder is the
|
|
19
|
+
-- railway composition of the steps: the first Left is the workflow's result.
|
|
20
|
+
workflow placeOrder :: UnvalidatedOrder -> Either OrderError PricedOrder is
|
|
21
|
+
validateOrder :: UnvalidatedOrder -> Either OrderError ValidatedOrder
|
|
22
|
+
priceOrder :: ValidatedOrder -> Either OrderError PricedOrder
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
firstLine :: NonEmptyList Int32 -> Int32
|
|
26
|
+
|
|
27
|
+
law `validation accepts exactly the valid orders` is
|
|
28
|
+
definition is
|
|
29
|
+
`for all` (input :: UnvalidatedOrder) .
|
|
30
|
+
(match validateOrder input with
|
|
31
|
+
| Left e -> false
|
|
32
|
+
| Right v -> true
|
|
33
|
+
end) = (match input with
|
|
34
|
+
| UnvalidatedOrder id quantity ->
|
|
35
|
+
prelude.length id > 0 && quantity >= 1 && quantity <= 1000
|
|
36
|
+
end)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
law `quantities are always in range` is
|
|
41
|
+
definition is
|
|
42
|
+
`for all` (q :: UnitQuantity) .
|
|
43
|
+
valueOfUnitQuantity q >= 1 && valueOfUnitQuantity q <= 1000
|
|
44
|
+
end
|
|
45
|
+
example `largest quantity` is
|
|
46
|
+
q = UnitQuantity 1000
|
|
47
|
+
expect valueOfUnitQuantity q = 1000
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
law `a non-empty list has a first element` is
|
|
52
|
+
definition is
|
|
53
|
+
`for all` (xs :: NonEmptyList Int32) .
|
|
54
|
+
(match valueOfNonEmptyList xs with
|
|
55
|
+
| Nil -> false
|
|
56
|
+
| Cons head tail -> firstLine xs == head
|
|
57
|
+
end) = true
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -19,6 +19,35 @@ append :: (xs :: Vec n Int8) -> (ys :: Vec m Int8) -> (r :: Vec (n + m) Int8)
|
|
|
19
19
|
zip :: (xs :: Vec n Int8) -> (ys :: Vec n Bool) -> (r :: Vec n Bool)
|
|
20
20
|
flatten :: (tree :: Tree n Int8) -> (r :: Vec n Int8)
|
|
21
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
|
+
|
|
22
51
|
law `replicate has the requested length` is
|
|
23
52
|
definition is
|
|
24
53
|
`for all` (n :: Natural where n < 16) (x :: Int8) . nOfVec (replicate n x) = n
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lawspec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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", "RELEASE-0.11.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", "RELEASE-0.13.md", "LICENSE", "starter.lawspec", "examples"],
|
|
12
12
|
"scripts": { "test": "node --test test/*.test.mjs" }
|
|
13
13
|
}
|