lawspec 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API-MIGRATION.md +102 -1
- package/GO.md +144 -0
- package/HASKELL.md +171 -0
- package/JAVA.md +113 -0
- package/KOTLIN.md +214 -0
- package/LANGUAGE.md +237 -12
- package/PRIMITIVES.md +1 -1
- package/PYTHON.md +152 -0
- package/README.md +26 -13
- package/REFINEMENTS.md +289 -3
- package/RELEASE-0.9.md +61 -0
- package/RUST.md +115 -1
- package/WEB.md +144 -0
- package/api.mjs +8 -2
- package/bin/lawspec.mjs +9 -6
- package/build.json +179 -37
- package/core.wasm +0 -0
- package/examples/specs/collections.lawspec +67 -0
- package/examples/specs/data_types.lawspec +66 -0
- package/examples/specs/finite_data.lawspec +37 -0
- package/examples/specs/list_contracts.lawspec +136 -0
- package/examples/specs/list_refinements.lawspec +51 -0
- package/examples/specs/matching.lawspec +49 -0
- package/examples/specs/recursive_refinements.lawspec +41 -0
- package/examples/specs/refined_definitions.lawspec +44 -0
- package/examples/specs/sum_refinements.lawspec +49 -0
- package/examples/specs/total_functions.lawspec +101 -0
- package/examples-command.mjs +6 -1
- package/files.mjs +10 -1
- package/index.d.ts +246 -27
- package/package.json +2 -2
- package/templates.mjs +107 -17
package/API-MIGRATION.md
CHANGED
|
@@ -1,11 +1,60 @@
|
|
|
1
1
|
# Compiler API migration: schema 2 → schema 3
|
|
2
2
|
|
|
3
|
-
LawSpec 0.8
|
|
3
|
+
LawSpec 0.8 introduced API schema version **3**, which 0.9 continues to use.
|
|
4
|
+
Requests may omit `schemaVersion` or
|
|
4
5
|
send `3`. An explicit `2` (or any other version) receives a request diagnostic;
|
|
5
6
|
it is never silently reinterpreted. LawSpec specification syntax remains compatible.
|
|
6
7
|
The generated `index.d.ts` describes the public protocol. Internal Haskell
|
|
7
8
|
constructors and record fields are no longer the wire format.
|
|
8
9
|
|
|
10
|
+
## 0.9 structural data and definition metadata
|
|
11
|
+
|
|
12
|
+
Successful results also expose `dataTypes: DataTypeDeclaration[]`. Each named
|
|
13
|
+
declaration has a resolved `id`, a display `name`, parameter IDs, an origin, and
|
|
14
|
+
constructors with resolved IDs and ordered typed fields. Use resolved IDs to
|
|
15
|
+
join references; constructors with the same display name can belong to different
|
|
16
|
+
units. Built-in container types do not need user declarations in this array.
|
|
17
|
+
|
|
18
|
+
Example bindings now use `DataValue`: either an existing tagged scalar or
|
|
19
|
+
`{kind: "data", type: Type, constructor: string, fields: DataValue[]}`. List values
|
|
20
|
+
use `List::Nil` and `List::Cons`, with head and tail fields; Maybe and Either use
|
|
21
|
+
their qualified constructor IDs. Do not flatten these values to JSON arrays or
|
|
22
|
+
nullable fields: that would lose constructor and nested-presence distinctions.
|
|
23
|
+
|
|
24
|
+
Expressions add `construct` nodes with a constructor ID and argument expressions,
|
|
25
|
+
and `match` nodes with a scrutinee and cases. Each case has a constructor ID,
|
|
26
|
+
ordered typed binders, and a body. Its binders are local to that case. Structural
|
|
27
|
+
expressions are represented by these nodes, not by scalar `constant` nodes.
|
|
28
|
+
Exhaustive API visitors must handle the added expression variants even though
|
|
29
|
+
the protocol continues to use schema version 3.
|
|
30
|
+
|
|
31
|
+
Successful results add `definitions: Definition[]`. Each entry contains `owner`,
|
|
32
|
+
the resolved declaration `id`, typed `arguments: Binder[]`, and a typed `body`.
|
|
33
|
+
The matching entry in `units[].declarations` supplies the signature and origin.
|
|
34
|
+
Calls retain their declaration ID; consumers can join against `definitions` to
|
|
35
|
+
distinguish checked bodies from external adapters. An empty array means the
|
|
36
|
+
program has no definitions.
|
|
37
|
+
|
|
38
|
+
The native frontend checks these bodies for typing, exhaustive matching,
|
|
39
|
+
structural termination, and potentially failing operations. Native reference
|
|
40
|
+
execution and source emission for Rust, Java, Kotlin, Python, JavaScript,
|
|
41
|
+
TypeScript, Go, and Haskell are implemented, including generic definitions
|
|
42
|
+
specialized to concrete signatures. The API exposes concrete instances with
|
|
43
|
+
generated names and resolved IDs, not unspecialized templates. Calls sharing a
|
|
44
|
+
signature reuse an instance; unused templates emit none. Consumers should join
|
|
45
|
+
by ID rather than parse generated names. Refinement-bearing definitions are
|
|
46
|
+
checked and emitted by both the native compiler and bundled WASM distribution.
|
|
47
|
+
Definition bodies produce generated source rather than
|
|
48
|
+
user-owned adapter stubs. Haskell native entry points take an `LS.SymbolContext`
|
|
49
|
+
and return `Either String a`; generated tests allocate a context per example or
|
|
50
|
+
property iteration so Symbol fixture identity does not escape its scope.
|
|
51
|
+
|
|
52
|
+
Refinement and contract expressions may now contain calls whose IDs resolve to
|
|
53
|
+
checked `definitions`. Validation still rejects calls to external adapters in
|
|
54
|
+
these expressions. Test planning evaluates the closed definitions when filtering
|
|
55
|
+
finite cases, boundaries, and concrete example inputs. Calls remain ordinary
|
|
56
|
+
typed call nodes; consumers need no source-level refinement interpreter.
|
|
57
|
+
|
|
9
58
|
## Laws and typed expressions
|
|
10
59
|
|
|
11
60
|
Read `result.laws[i].examples` instead of `law.original.examples`. A binding is
|
|
@@ -101,3 +150,55 @@ All nonempty test plans now emit the portable scalar runtime. Existing Haskell
|
|
|
101
150
|
projects must include `text` and `bytestring` in the component that compiles
|
|
102
151
|
that source. Doctor reports the missing dependencies before generation. Build
|
|
103
152
|
files remain user-owned; new scaffolds already include these dependencies.
|
|
153
|
+
|
|
154
|
+
## Formatting requests and adapter references (0.9)
|
|
155
|
+
|
|
156
|
+
`GenerationRequest` accepts `minify?: boolean`, defaulting to `false`. The CLI
|
|
157
|
+
passes an explicit `--minify` from `generate` and `examples`. `init --minify`
|
|
158
|
+
also compacts newly created scaffolds and the configuration JSON; the choice is
|
|
159
|
+
not saved as a project setting. Existing project build files stay user-owned. Source/test placement is independent of
|
|
160
|
+
formatting. Compact rendering preserves mandatory newlines, indentation, token
|
|
161
|
+
separators, comments, and literal contents.
|
|
162
|
+
|
|
163
|
+
User-owned artifacts may include `adapterReference`, the compiler's canonical
|
|
164
|
+
readable scaffold. It is comparison data, not the user's implementation and not
|
|
165
|
+
an additional file to write. Manifest writers should hash this reference when
|
|
166
|
+
present, falling back to `content` for older producers. Continue hashing actual
|
|
167
|
+
`content` for generated-file ownership. This keeps a switch of formatting mode
|
|
168
|
+
from producing false adapter-update reports, while declared interface changes
|
|
169
|
+
still request review. Never normalize, overwrite, or hash user implementations
|
|
170
|
+
as the required adapter interface. Existing version-1 manifests remain readable.
|
|
171
|
+
|
|
172
|
+
Native and bundled WASM requests share this formatting behavior. CLI scaffolds,
|
|
173
|
+
generated sources, and tests preserve the same ownership rules in both modes.
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
Scoped List payload predicates use the schema-3 expression node
|
|
177
|
+
`{kind: "allElements", value: Expr, binder: Binder, predicate: Expr}`. The binder
|
|
178
|
+
is local to `predicate`; `value` is evaluated in the surrounding scope. The
|
|
179
|
+
predicate and result have type Bool. Visitors must handle this node alongside
|
|
180
|
+
`match`, including empty-list truth and short-circuit evaluation.
|
|
181
|
+
|
|
182
|
+
The Core also defines a scoped recursive payload operation:
|
|
183
|
+
`{kind: "allPayloads", value: Expr, predicates: PayloadPredicate[]}`, where each
|
|
184
|
+
`PayloadPredicate` contains a `binder: Binder` and `predicate: Expr`. Entries
|
|
185
|
+
correspond, in order, to the root data type's type arguments. Each binder has
|
|
186
|
+
that argument's type and is local only to its own predicate; sibling predicates
|
|
187
|
+
cannot refer to it. The scrutinee is evaluated in the surrounding scope, and
|
|
188
|
+
both each predicate and the whole operation have type Bool.
|
|
189
|
+
|
|
190
|
+
Traversal follows stored parameter occurrences through recursive declarations,
|
|
191
|
+
including nested containers and changing type arguments. It does not constrain
|
|
192
|
+
unrelated fixed fields that happen to have the same concrete type. Empty and
|
|
193
|
+
phantom occurrences are vacuously true; rejection short-circuits traversal.
|
|
194
|
+
|
|
195
|
+
Source named-payload refinements now elaborate to this discriminator, including
|
|
196
|
+
recursive applications. API consumers should handle it in checked Core views.
|
|
197
|
+
All eight Core emitters support this operation in definitions, properties and
|
|
198
|
+
constructor predicates.
|
|
199
|
+
The internal surface predicate node is type checked and lowered through
|
|
200
|
+
specialization and template proofs into this same Core operation. Internal
|
|
201
|
+
definition and constructor contracts support recursive payload proof facts. Constructor predicates are audited in order, and callback
|
|
202
|
+
matches/constructions participate in the constructor dependency-cycle check.
|
|
203
|
+
The TypeScript declarations now include both
|
|
204
|
+
`allElements` and `allPayloads`; exhaustive visitors should handle both.
|
package/GO.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Native Go data
|
|
2
|
+
|
|
3
|
+
LawSpec 0.9 generates native named Go types for parameterized products and sums.
|
|
4
|
+
The compiler consumes checked Core declarations, plans names across all units,
|
|
5
|
+
and emits sealed interfaces plus named variant structs, following Go+'s enum
|
|
6
|
+
lowering approach.
|
|
7
|
+
|
|
8
|
+
For example:
|
|
9
|
+
|
|
10
|
+
```lawspec
|
|
11
|
+
unit example.trees
|
|
12
|
+
|
|
13
|
+
type Tree (a :: Type) is
|
|
14
|
+
Leaf value :: a
|
|
15
|
+
Branch children :: List (Tree a)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
echo :: Tree Int8 -> Tree Int8
|
|
19
|
+
|
|
20
|
+
law `echo preserves the tree` is
|
|
21
|
+
definition is `for all` (x :: Tree Int8) . echo x = x end
|
|
22
|
+
end
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The adapter receives `Tree[int8]`. Its native variants are `TreeLeaf[int8]`,
|
|
26
|
+
with an exported `Value` field, and `TreeBranch[int8]`, with an exported
|
|
27
|
+
`Children []Tree[int8]` field. The interface's private marker method includes
|
|
28
|
+
the type parameters, so even a nullary or phantom variant retains its generic
|
|
29
|
+
identity. Go rejects a `TreeLeaf[bool]` where `Tree[int8]` is required.
|
|
30
|
+
|
|
31
|
+
A correct identity adapter is:
|
|
32
|
+
|
|
33
|
+
```go
|
|
34
|
+
func Echo(value0 Tree[int8]) Tree[int8] {
|
|
35
|
+
return value0
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Adapters remain user-owned. Generated files belong to the same unit package;
|
|
40
|
+
custom Go layouts keep source and tests in a shared directory tree. Duplicate
|
|
41
|
+
type names across units receive qualified generated names. The compiler rejects
|
|
42
|
+
adapter names that collide with generated native declarations or runtime names.
|
|
43
|
+
|
|
44
|
+
## Containers and presence
|
|
45
|
+
|
|
46
|
+
- `List a` uses `[]T`. Nil and empty slices represent the same empty list.
|
|
47
|
+
- `Maybe a` uses `LawSpecMaybe[T]`, constructed with `LawSpecNothing[T]()` or
|
|
48
|
+
`LawSpecJust(value)`. `Value()` returns the payload and its presence flag.
|
|
49
|
+
- `Either a b` uses `LawSpecEither[L, R]`, constructed with
|
|
50
|
+
`LawSpecLeft[L, R](value)` or `LawSpecRight[L, R](value)`. Its zero value is
|
|
51
|
+
invalid and checked conversion rejects it.
|
|
52
|
+
- `Nullable a` and `Optional a` use distinct generic support structs with
|
|
53
|
+
`Present` and `Value` fields. These tags preserve nested absence states.
|
|
54
|
+
|
|
55
|
+
Native scalar fields retain their domains: UInt64 uses `uint64`, arbitrary
|
|
56
|
+
integers use `*LawSpecBigInt`, rational values use `*LawSpecRational`, and raw
|
|
57
|
+
UTF-16 text uses `[]uint16`. Text uses a valid UTF-8 Go string; arbitrary bytes
|
|
58
|
+
use `[]byte`. Unit, Null, and Undefined have distinct named support types when
|
|
59
|
+
stored inside data. Native void adapter results continue to normalize to Unit.
|
|
60
|
+
|
|
61
|
+
## Total definitions
|
|
62
|
+
|
|
63
|
+
Checked source definitions emit native methods on `LawSpecDefinitions` in the
|
|
64
|
+
unit's package:
|
|
65
|
+
|
|
66
|
+
```lawspec
|
|
67
|
+
unit example.total
|
|
68
|
+
|
|
69
|
+
definition increment (x :: Int8) :: BigInt is x + 1 end
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```go
|
|
73
|
+
symbols := map[string]*LawSpecSymbol{}
|
|
74
|
+
result := LawSpecDefinitions.Increment(symbols, 127) // big integer 128
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The method accepts `int8` and returns `*LawSpecBigInt`. Lists, products, sums, and
|
|
78
|
+
nested presence retain their native parameterized types. A dedicated method
|
|
79
|
+
namespace allows a definition named `architecture` to coexist with the native
|
|
80
|
+
`Architecture` data type. Ordinary adapter functions retain their existing names.
|
|
81
|
+
|
|
82
|
+
Reuse the Symbol context within one example. Checked codecs copy and validate
|
|
83
|
+
native arguments and results; failures panic with the resolved definition name.
|
|
84
|
+
Machine-sized native bindings check the architecture across the complete type,
|
|
85
|
+
including alternatives not selected by the current value. Logical evaluation
|
|
86
|
+
still follows the explicitly selected machine profile.
|
|
87
|
+
|
|
88
|
+
`lawspec_definitions.go` belongs in source directories and does not import Rapid.
|
|
89
|
+
It contains the unit's native entry points and checked logical bodies. Properties
|
|
90
|
+
call the bodies directly; definitions never become user-owned adapter stubs.
|
|
91
|
+
Definitions and properties share typed expression rendering, preserving lazy
|
|
92
|
+
branches, short-circuit guards, single evaluation of match inputs, exact
|
|
93
|
+
arithmetic, and structural equality.
|
|
94
|
+
|
|
95
|
+
The frontend checks structural termination and potentially failing operations.
|
|
96
|
+
Generic definitions specialize to concrete uses. Refined signatures become
|
|
97
|
+
checked contracts, and refinement predicates may call checked definitions.
|
|
98
|
+
|
|
99
|
+
`tools/go-definitions-integration.mjs` checks source-only packages, four rejected
|
|
100
|
+
native type mismatches, both profiles and architecture diagnostics, recursive
|
|
101
|
+
properties, incorrect adapters, custom layouts, compact execution, and
|
|
102
|
+
regeneration protection. Readable definition source matches `gofmt` exactly.
|
|
103
|
+
Compact Go documents retain tabs rather than expanding them into spaces.
|
|
104
|
+
|
|
105
|
+
## Checked bridges and tests
|
|
106
|
+
|
|
107
|
+
Generated codecs validate both conversion directions and copy mutable payloads.
|
|
108
|
+
An adapter cannot mutate a list, byte slice, or big integer in a test fixture
|
|
109
|
+
through an input alias. Unexpected or nil native variants, invalid scalar
|
|
110
|
+
representations, and cyclic values fail with type/field context. Shared acyclic
|
|
111
|
+
subtrees remain valid. Native machine-sized fields require the selected
|
|
112
|
+
`machineBits` profile to match the Go architecture.
|
|
113
|
+
|
|
114
|
+
Generated equality follows LawSpec semantics, including componentwise floating
|
|
115
|
+
comparison and Symbol identity. It does not substitute Go pointer identity or
|
|
116
|
+
reflection-based equality for structural equality.
|
|
117
|
+
|
|
118
|
+
Rapid generation composes native generators and shrinkers. A structural node
|
|
119
|
+
budget bounds recursive values, reserves each product field's minimum cost,
|
|
120
|
+
and permits list lengths supported by the remaining budget. Empty domains,
|
|
121
|
+
nullary constructors, and nested absence states are handled explicitly.
|
|
122
|
+
|
|
123
|
+
The reusable source files (`lawspec_runtime.go`, `lawspec_schema.go`,
|
|
124
|
+
`lawspec_codecs.go`, and generated data/schema/codec declarations) do not import
|
|
125
|
+
Rapid. Framework-specific generation lives in
|
|
126
|
+
`lawspec_data_strategies_test.go`; generated laws live in `lawspec_test.go`.
|
|
127
|
+
|
|
128
|
+
Native declarations, schema descriptions, codecs, runtime support, and generated
|
|
129
|
+
tests use structured formatting that matches `gofmt`. The bundled examples and
|
|
130
|
+
total-definition fixture are checked against `gofmt` under both machine profiles.
|
|
131
|
+
No external formatter is needed when generating code.
|
|
132
|
+
|
|
133
|
+
The compiler and CLI accept explicit `--minify` for generated output.
|
|
134
|
+
Readable output remains the default; formatting changes preserve adapter ownership
|
|
135
|
+
and edited-file protection. The bundled WASM uses the same layout.
|
|
136
|
+
|
|
137
|
+
Internal typed Core definitions with attached contracts are proved before Go
|
|
138
|
+
emission. Generated logical entry points validate arguments, check preconditions
|
|
139
|
+
in order, validate the result, and check postconditions; native wrappers share
|
|
140
|
+
these checks. The standalone contract fixture exercises both machine profiles
|
|
141
|
+
and formatting modes, including exact division, checked narrowing, nested calls,
|
|
142
|
+
and rejection of deliberately corrupted results. Readable contract bodies match
|
|
143
|
+
`gofmt`. Refined source definition signatures now produce these contracts through
|
|
144
|
+
template proof and specialization.
|
package/HASKELL.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Native Haskell data
|
|
2
|
+
|
|
3
|
+
LawSpec 0.9 emits ordinary parameterized algebraic data types from checked Core
|
|
4
|
+
products and sums. Constructors and record selectors have stable names planned
|
|
5
|
+
across all units. For example:
|
|
6
|
+
|
|
7
|
+
```lawspec
|
|
8
|
+
unit example.trees
|
|
9
|
+
|
|
10
|
+
type Tree (a :: Type) is
|
|
11
|
+
Leaf value :: a
|
|
12
|
+
Branch children :: List (Tree a)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
echo :: Tree Int8 -> Tree Int8
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The generated `LawSpecData` module supplies `Tree a`, `TreeLeaf`, and
|
|
19
|
+
`TreeBranch`. An adapter can implement identity directly:
|
|
20
|
+
|
|
21
|
+
```haskell
|
|
22
|
+
echo :: Data.Tree I.Int8 -> Data.Tree I.Int8
|
|
23
|
+
echo value = value
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The generated adapter imports `LawSpecData` as `Data` and `Data.Int` as `I`.
|
|
27
|
+
Adapters remain user-owned. Recursive and mutually recursive fields use native
|
|
28
|
+
Haskell recursion; phantom parameters remain in signatures. Empty data types
|
|
29
|
+
have no constructors. They can appear in inhabited containers such as
|
|
30
|
+
`Maybe Empty`, but cannot supply a standalone generated argument.
|
|
31
|
+
|
|
32
|
+
## Containers and scalar representations
|
|
33
|
+
|
|
34
|
+
| LawSpec | Haskell |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `List a` | `[a]` |
|
|
37
|
+
| `Text` | `Data.Text.Text` |
|
|
38
|
+
| `List Char` | `[Char]`, the linked-list representation |
|
|
39
|
+
| `Maybe a` | `Maybe a`, with `Nothing` and `Just` |
|
|
40
|
+
| `Either a b` | `Either a b`, with `Left` and `Right` |
|
|
41
|
+
| `Nullable a` | `LS.Nullable a`, with `NullValue` and `NullableValue` |
|
|
42
|
+
| `Optional a` | `LS.Optional a`, with `UndefinedValue` and `OptionalValue` |
|
|
43
|
+
| `BigInt`, `BigUInt`, `Integer` | `Integer`, with domain checks |
|
|
44
|
+
| `Decimal` | `LS.Decimal`, wrapping an exact finite base-ten `Rational` |
|
|
45
|
+
| `Rational` | `Rational` |
|
|
46
|
+
| `Complex64`, `Complex128` | `Complex Float`, `Complex Double` |
|
|
47
|
+
| `CodePoint` | `Char`, including surrogate code points |
|
|
48
|
+
| `CodeUnit16` | `Word16` |
|
|
49
|
+
| `CodePointText`, `Utf16Text` | `LS.CodePointText [Char]`, `LS.Utf16Text [Word16]` |
|
|
50
|
+
| `Bytes` | `ByteString` |
|
|
51
|
+
| `Symbol` | `LS.Symbol`, with scoped fixture identities |
|
|
52
|
+
| `Unit` | `()` |
|
|
53
|
+
| `Null`, `Undefined` | `LS.Null`, `LS.Undefined` |
|
|
54
|
+
|
|
55
|
+
`Char` and `Text` exclude surrogate code points. Checked codecs reject invalid
|
|
56
|
+
native values rather than replacing characters. `Symbol` equality uses its
|
|
57
|
+
identity; matching descriptions alone do not establish equality. Distinct
|
|
58
|
+
presence wrappers preserve nested absence states. IEEE NaN and signed-zero
|
|
59
|
+
equality remain the same inside containers and custom types.
|
|
60
|
+
|
|
61
|
+
## Generated support and testing
|
|
62
|
+
|
|
63
|
+
`LawSpecData`, `LawSpecDataSchema`, `LawSpecDataCodecs`, `LawSpecSchema`,
|
|
64
|
+
`LawSpecCodecs`, and `LawSpecRuntime` belong in the source directory. They have no
|
|
65
|
+
property-framework dependency. Native conversion returns contextual failures for
|
|
66
|
+
invalid domains, fields, tags, or arities. Binding machine-sized native types
|
|
67
|
+
requires the selected `machineBits` profile to match the host, including fields
|
|
68
|
+
of unselected variants.
|
|
69
|
+
|
|
70
|
+
`LawSpecDataStrategies` belongs in the test directory and composes native Hedgehog
|
|
71
|
+
generators and shrinkers. Bounded recursive generation reserves every product
|
|
72
|
+
field's minimum size before distributing spare nodes. Lists have variable lengths
|
|
73
|
+
within the available budget. Shrinking uses Hedgehog's choices, integers, and
|
|
74
|
+
lists, retaining schema-valid representations.
|
|
75
|
+
|
|
76
|
+
The Haskell scaffold uses Hspec, Hedgehog, hspec-hedgehog, containers, and mtl in
|
|
77
|
+
its test component. Source and test roots can be customized independently.
|
|
78
|
+
Readable/compact declaration fixtures are covered by
|
|
79
|
+
`tools/haskell-data-integration.mjs`; main compiler output and incorrect adapters
|
|
80
|
+
are covered by `tools/haskell-data-properties.mjs`.
|
|
81
|
+
|
|
82
|
+
## Formatting
|
|
83
|
+
|
|
84
|
+
Haskell output uses an 80-column layout with spaces for indentation. Runtime
|
|
85
|
+
sources, native declarations, adapter stubs, definitions, and property files are
|
|
86
|
+
readable by default. Explicit `--minify` selects compact documents while retaining
|
|
87
|
+
the layout required by Haskell. Generation uses the same deterministic document
|
|
88
|
+
renderer in native and WASM builds; it does not invoke a downloaded formatter.
|
|
89
|
+
|
|
90
|
+
`tools/haskell-formatting-integration.mjs` checks the bundled corpus at both
|
|
91
|
+
machine widths for line length, tabs, and trailing whitespace. It compares
|
|
92
|
+
readable and compact parsed syntax using `tools/HaskellSyntaxCheck.hs`, built
|
|
93
|
+
against the installed GHC parser. The comparison discards source locations and
|
|
94
|
+
layout annotations while retaining literals, operators, and program structure.
|
|
95
|
+
Set `LAWSPEC_CORE`, `LAWSPEC_GHC`, and `LAWSPEC_HASKELL_SYNTAX_CHECK` to the
|
|
96
|
+
corresponding executables. This is a development check, not a package dependency.
|
|
97
|
+
|
|
98
|
+
## Total definitions
|
|
99
|
+
|
|
100
|
+
Checked definitions become native functions in `LawSpecDefinitions.<Unit>`:
|
|
101
|
+
|
|
102
|
+
```lawspec
|
|
103
|
+
unit example.total
|
|
104
|
+
|
|
105
|
+
definition increment (x :: Int8) :: BigInt is x + 1 end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The generated native signature is equivalent to:
|
|
109
|
+
|
|
110
|
+
```haskell
|
|
111
|
+
increment :: LS.SymbolContext -> I.Int8 -> Either String Integer
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Create a context with `LS.newSymbolContext`, then pass it to calls belonging to
|
|
115
|
+
the same example. A repeated Symbol fixture ID has the same identity in that
|
|
116
|
+
context; separate contexts remain distinct even when IDs and descriptions match.
|
|
117
|
+
Codecs preserve the identity of already-scoped native Symbols. Creating the
|
|
118
|
+
context uses IO; evaluating a definition is pure.
|
|
119
|
+
|
|
120
|
+
Native entry points check arguments and results and return contextual `Left`
|
|
121
|
+
diagnostics for invalid native representations. Native machine-sized bindings
|
|
122
|
+
check the whole type, including unselected constructors, against `machineBits`.
|
|
123
|
+
`LawSpecDefinitionBodies` contains the shared logical implementation, which uses
|
|
124
|
+
the configured machine profile independently of the host architecture.
|
|
125
|
+
|
|
126
|
+
Definitions do not create adapter stubs. Their source modules and scalar/schema
|
|
127
|
+
support have no Hspec or Hedgehog dependency. Generated tests allocate a context
|
|
128
|
+
per example, boundary, or property iteration. Properties and definitions share a
|
|
129
|
+
typed expression renderer with lazy guards, exhaustive matching, structural
|
|
130
|
+
equality, checked conversions, and exact integer promotion.
|
|
131
|
+
|
|
132
|
+
`tools/haskell-definitions-integration.mjs` exercises native and property calls,
|
|
133
|
+
both machine profiles, readable/compact definitions, complete minified projects,
|
|
134
|
+
custom source/test roots,
|
|
135
|
+
native type errors, incorrect adapters, generic specialization, and regeneration
|
|
136
|
+
protection. Refined definition signatures are checked before emission and
|
|
137
|
+
enforced at native entry points.
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
Hspec property files now use structured documents for helpers, examples,
|
|
141
|
+
boundaries/finite cases, native Hedgehog strategies, dependent refinement domains,
|
|
142
|
+
guarded assertions and contract wrappers. Adapter bridges compose checked codecs
|
|
143
|
+
and force argument values before calling native code. Contracts retain lazy
|
|
144
|
+
precondition checks and force the result before checking postconditions. Fresh
|
|
145
|
+
Symbol contexts cover refined properties as well as ordinary native strategies.
|
|
146
|
+
Scalar literals and diagnostic strings now use document-level encoding. Long
|
|
147
|
+
strings concatenate independently escaped chunks; large integers parse exact
|
|
148
|
+
decimal strings with their required Integer type.
|
|
149
|
+
|
|
150
|
+
`tools/haskell-data-properties.mjs` accepts `LAWSPEC_MINIFY=1` and
|
|
151
|
+
`LAWSPEC_MACHINE_BITS=32,64`. Its scalar scenario includes bundled examples and
|
|
152
|
+
shared arithmetic conformance vectors; its refinements scenario includes four
|
|
153
|
+
faulty adapters. Native machine-sized adapter bindings require a matching host
|
|
154
|
+
architecture; the scalar scenario defaults to 64 bits.
|
|
155
|
+
|
|
156
|
+
`tools/haskell-message-integration.mjs` uses native code-point strings and integer
|
|
157
|
+
constants independent of the emitter to check Unicode, escapes, whitespace,
|
|
158
|
+
large signed integers and diagnostic prefixes in readable and compact modes.
|
|
159
|
+
Its executable fixture lines fit within 80 columns. Long metadata comments wrap
|
|
160
|
+
to the output width.
|
|
161
|
+
|
|
162
|
+
Internal typed Core definitions with attached contracts are proved before Haskell
|
|
163
|
+
emission. Generated source checks preconditions after argument validation and
|
|
164
|
+
postconditions after result validation, without property-framework dependencies.
|
|
165
|
+
Checks sequence through Either, so a rejected precondition prevents later
|
|
166
|
+
predicates and the body from running. Results are forced and validated before
|
|
167
|
+
postconditions. Readable contract fixtures fit within 80 columns.
|
|
168
|
+
The shared native fixture passes both machine profiles and formatting modes,
|
|
169
|
+
including exact division, narrowing, nested calls, direct logical entry checks,
|
|
170
|
+
and rejection of corrupted results. Refined source definition signatures now
|
|
171
|
+
produce these contracts through template proof and specialization.
|
package/JAVA.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Java backend (0.9)
|
|
2
|
+
|
|
3
|
+
Java targets Java 25+, JUnit, and JetCheck. The compiler emits native named
|
|
4
|
+
products and sums, checked scalar/data codecs, reusable runtime sources, and
|
|
5
|
+
separate property-test helpers. Native type parameters remain visible in public
|
|
6
|
+
data declarations and adapter signatures.
|
|
7
|
+
|
|
8
|
+
## Total definitions
|
|
9
|
+
|
|
10
|
+
A unit-level definition supplies its implementation together with its signature:
|
|
11
|
+
|
|
12
|
+
```lawspec
|
|
13
|
+
unit example.total
|
|
14
|
+
|
|
15
|
+
definition size (xs :: List Int8) :: BigInt is
|
|
16
|
+
match xs with
|
|
17
|
+
| Nil -> 0
|
|
18
|
+
| Cons head tail -> 1 + size tail
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The compiler checks the body even when no law uses it. It requires exhaustive
|
|
24
|
+
matching, established structural descent for recursion, and proof that partial
|
|
25
|
+
operations are defined. Calls may target other checked definitions, including
|
|
26
|
+
forward declarations; definitions cannot call external adapters. Generic definitions specialize to concrete uses. Refined signatures become
|
|
27
|
+
checked contracts, and refinement predicates may call checked definitions.
|
|
28
|
+
|
|
29
|
+
Java writes native entry points beneath `lawspec.definitions`, preserving the
|
|
30
|
+
unit's package and class mapping. The example above provides
|
|
31
|
+
`lawspec.definitions.example.Total.size`:
|
|
32
|
+
|
|
33
|
+
```java
|
|
34
|
+
var symbols = new java.util.HashMap<String, Object>();
|
|
35
|
+
var count = lawspec.definitions.example.Total.size(symbols, java.util.List.of((byte) 1, (byte) 2));
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Arguments and results use the same checked native representations as generated
|
|
39
|
+
data codecs: `List<Byte>` and `BigInteger` in this example. Domains without a
|
|
40
|
+
faithful Java representation retain their tagged runtime support values,
|
|
41
|
+
including nested Nullable/Optional states and machine-profile integers. Share
|
|
42
|
+
the Symbol map when fixture IDs should refer to the same identity.
|
|
43
|
+
|
|
44
|
+
`LawSpecDefinitionBodies.java` contains generated checked implementation helpers.
|
|
45
|
+
Generated properties call those same bodies. The native entry points validate
|
|
46
|
+
and copy values through codecs, and invalid native values report the definition
|
|
47
|
+
identity in an `IllegalArgumentException`. Logical machine integers enforce the
|
|
48
|
+
selected 32- or 64-bit range independently of the JVM architecture.
|
|
49
|
+
|
|
50
|
+
These files belong to source directories and have no JUnit or JetCheck dependency.
|
|
51
|
+
Definitions never receive user-owned adapter stubs. External declarations still
|
|
52
|
+
receive stubs, and regeneration preserves edited adapters. Edits to generated
|
|
53
|
+
definition files are protected by the generation manifest.
|
|
54
|
+
|
|
55
|
+
## Verification and layout
|
|
56
|
+
|
|
57
|
+
`tools/java-definitions-integration.mjs` executes recursive list/tree definitions,
|
|
58
|
+
forward calls, scalars, sums, raw code units, Symbol identity, and nested absence
|
|
59
|
+
under both profiles. It checks custom source/test roots, typed native misuse,
|
|
60
|
+
incorrect adapters, framework-independent compilation, and ownership.
|
|
61
|
+
|
|
62
|
+
Readable generated sources and tests use structured formatting that matches
|
|
63
|
+
Google Java Format. `tools/java-formatting-integration.mjs` checks all bundled
|
|
64
|
+
examples and the total-definition fixture under both machine profiles. The
|
|
65
|
+
compiler emits this layout directly; generation does not invoke a formatter.
|
|
66
|
+
JetCheck combinators retain native shrinking and the existing scalar domains.
|
|
67
|
+
|
|
68
|
+
The CLI/API accept explicit minify. Native integration checks run
|
|
69
|
+
both readable and fully minified generation plans, including properties and
|
|
70
|
+
contracts. Independent Java execution checks preserve long diagnostics,
|
|
71
|
+
supplementary Unicode, escaping and arbitrary integers when literals wrap.
|
|
72
|
+
The bundled WASM package is checked against the native compiler.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
For internal checked Core definitions, shared JVM implementation bodies now
|
|
76
|
+
prove attached contracts before emission and enforce ordered preconditions and
|
|
77
|
+
validated-result postconditions at runtime. Native wrappers and direct logical
|
|
78
|
+
entry points both use those checks. `tools/jvm-definition-contract-integration.mjs`
|
|
79
|
+
verifies both JVM languages, profiles and layouts without a test-framework
|
|
80
|
+
dependency, including corrupted-result rejection. Refined source signatures
|
|
81
|
+
now produce these contracts through template proof and specialization.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
## Constructor contracts
|
|
85
|
+
|
|
86
|
+
Public Java generation supports typed constructor callbacks, context-aware codecs
|
|
87
|
+
and definition boundaries. Property emission supplies validated witnesses and one
|
|
88
|
+
Symbol context shared by generation, adapter conversions and assertions.
|
|
89
|
+
Required conjunctive Symbol equalities can use fixture/prior-input values;
|
|
90
|
+
equalities beneath disjunctions contribute candidates without restricting the
|
|
91
|
+
whole domain to one alternative.
|
|
92
|
+
|
|
93
|
+
The test helper's `checkedGenerator` composes JetCheck generators with native
|
|
94
|
+
`suchThat` filtering at nested and outer value boundaries. False predicates reject
|
|
95
|
+
candidates; predicate evaluation errors are retained for the property to report.
|
|
96
|
+
Each filter respects the smaller of `maxAttempts` and JetCheck 0.3's native
|
|
97
|
+
100-attempt limit. The counter for smaller budgets is recreated on replay, so
|
|
98
|
+
native filtering still discards invalid shrinks. Validated witnesses contribute typed nested seeds. Sampled seeds
|
|
99
|
+
can limit payload shrinking; native list and constructor generation remains an
|
|
100
|
+
alternative, and shrinking does not promise a globally minimal counterexample.
|
|
101
|
+
|
|
102
|
+
`tools/java-checked-strategies.mjs` verifies valid generation/shrinking, nested
|
|
103
|
+
witness use, exhaustion, error classification and shared Symbol identity at both
|
|
104
|
+
machine widths, with compiled behavioral mutants. These helpers depend on
|
|
105
|
+
JetCheck; the value/schema/codec runtime remains framework-independent.
|
|
106
|
+
|
|
107
|
+
Constructor-contract properties run the requested case count as one-iteration
|
|
108
|
+
JetCheck sessions with increasing size hints. This preserves native shrinking and
|
|
109
|
+
avoids session-wide draw-uniqueness exhaustion for fixture-identity domains. It
|
|
110
|
+
does not claim those domains are finite; domains proven finite by Core retain
|
|
111
|
+
exhaustive cases. `tools/java-field-properties.mjs` executes public generation at
|
|
112
|
+
both widths and layouts, including custom placement, adapter mutants, disjunctive
|
|
113
|
+
Symbol candidates and generation-time evaluator errors.
|