@fraczak/k 6.1.1 → 6.1.3
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/.codex +0 -0
- package/Code-derivation-tests/pattern-envelope.mjs +26 -0
- package/DOCS/BIG_PICTURE.md +10 -10
- package/DOCS/DIVERGENCE_DETECTION.md +1 -1
- package/DOCS/REPL.md +33 -0
- package/DOCS/TYPE_DERIVATION.md +12 -12
- package/DOCS/a.html +398 -0
- package/DOCS/book/01-introduction.md +2 -2
- package/DOCS/book/02-syntax-and-values.md +8 -8
- package/DOCS/book/03-types-as-finite-automata.md +2 -2
- package/DOCS/book/04-partial-functions-and-composition.md +2 -2
- package/DOCS/book/05-typing-filters-and-normalization.md +3 -3
- package/DOCS/book/06-values-in-memory.md +3 -3
- package/DOCS/book/07-the-partial-function-abi.md +1 -1
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/appendix-b-incorporating-external-predefined-types-and-functions.md +1 -0
- package/DOCS/dependency-graph.html +140 -0
- package/DOCS/dependency-graph.md +144 -0
- package/NAT.k~ +452 -0
- package/README.md +7 -8
- package/UU/ta.k +1 -0
- package/UU/ta.k~ +0 -0
- package/UU/tb.k +0 -0
- package/UU/tc.k +0 -0
- package/a.k +138 -0
- package/a.klib +1 -0
- package/codecs/BINARY_FORMAT.html +471 -0
- package/codecs/BINARY_FORMAT.md +1 -1
- package/codecs/POLYMORPHIC_BINARY_FORMAT.html +355 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.md +1 -1
- package/codecs/ieee.mjs +19 -4
- package/codecs/int.mjs +11 -4
- package/codecs/json.mjs +7 -4
- package/codecs/k-parse.mjs +7 -4
- package/codecs/k-print.mjs +7 -4
- package/codecs/runtime/cli-entry.mjs +13 -0
- package/codecs/show.mjs +7 -4
- package/codecs/unit.mjs +23 -8
- package/codecs/utf16.mjs +27 -5
- package/codecs/utf8.mjs +27 -5
- package/codes.mjs +14 -33
- package/core.json +1 -0
- package/core.klib +1 -0
- package/core.klib~ +0 -0
- package/export.mjs +3 -3
- package/ieee.klib +1 -0
- package/k-vscode/package-lock.json +4176 -0
- package/kkk.k +197 -0
- package/kkk.klib +0 -0
- package/kkk.klib~ +0 -0
- package/nak.klib +1 -0
- package/nak2.klib +1 -0
- package/nat.k +10 -0
- package/nat2.k +9 -0
- package/object.mjs +20 -7
- package/p.json +5475 -0
- package/package.json +3 -2
- package/repl-codecs.mjs +173 -0
- package/repl.mjs +215 -5
- package/run.mjs +16 -2
- package/scripts/benchmark-idempotency.mjs +72 -0
- package/test-ieee-arithmetic.mjs +93 -0
- package/test-k-object.mjs +19 -0
- package/test-repl.mjs +60 -1
- package/tests.sh +10 -1
- package/type_registry/registry.mjs~ +105 -0
- package/type_registry/typeParser.jison~ +132 -0
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/copilot.data.migration.agent.xml +0 -6
- package/.idea/copilot.data.migration.ask.xml +0 -6
- package/.idea/copilot.data.migration.ask2agent.xml +0 -6
- package/.idea/copilot.data.migration.edit.xml +0 -6
- package/.idea/k.iml +0 -9
- package/.idea/misc.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- package/k_compiler/runtime.c +0 -205
- package/k_compiler/runtime.h +0 -34
package/.codex
ADDED
|
File without changes
|
|
@@ -44,6 +44,32 @@ assert.deepEqual(decodedMerged.pattern, INT_PATTERN);
|
|
|
44
44
|
const identityResult = k.compile("()")(withPattern(two, INT_PATTERN));
|
|
45
45
|
assert.deepEqual(identityResult.pattern, INT_PATTERN);
|
|
46
46
|
|
|
47
|
+
const wrappedFloatPattern = [
|
|
48
|
+
["closed-product", [["result", 1], ["flags", 6]]],
|
|
49
|
+
["closed-product", [["sign", 2], ["exponent", 3], ["fraction", 4]]],
|
|
50
|
+
["closed-union", [["+", 5], ["-", 5]]],
|
|
51
|
+
["closed-product", [["0", 2]]],
|
|
52
|
+
["closed-product", [["0", 2]]],
|
|
53
|
+
["closed-product", []],
|
|
54
|
+
["closed-union", [["inexact", 5]]]
|
|
55
|
+
];
|
|
56
|
+
const wrappedFloat = new Product({
|
|
57
|
+
result: new Product({
|
|
58
|
+
sign: new Variant("+", new Product({})),
|
|
59
|
+
exponent: new Product({ 0: new Variant("1", new Product({})) }),
|
|
60
|
+
fraction: new Product({ 0: new Variant("0", new Product({})) })
|
|
61
|
+
}),
|
|
62
|
+
flags: new Variant("inexact", new Product({}))
|
|
63
|
+
});
|
|
64
|
+
const resultProjection = k.compile(".result")(withPattern(wrappedFloat, wrappedFloatPattern));
|
|
65
|
+
assert.deepEqual(resultProjection.pattern, [
|
|
66
|
+
["closed-product", [["sign", 1], ["exponent", 3], ["fraction", 4]]],
|
|
67
|
+
["closed-union", [["+", 2], ["-", 2]]],
|
|
68
|
+
["closed-product", []],
|
|
69
|
+
["closed-product", [["0", 1]]],
|
|
70
|
+
["closed-product", [["0", 1]]]
|
|
71
|
+
]);
|
|
72
|
+
|
|
47
73
|
assert.throws(
|
|
48
74
|
() => k.compile(".x")(withPattern(two, INT_PATTERN)),
|
|
49
75
|
/Type Error in 'comp'.*Value envelope does not intersect expression input pattern/s
|
package/DOCS/BIG_PICTURE.md
CHANGED
|
@@ -12,7 +12,7 @@ with that tree.
|
|
|
12
12
|
|
|
13
13
|
### Types (codes)
|
|
14
14
|
|
|
15
|
-
Formally, a _type_ is a
|
|
15
|
+
Formally, a _type_ is a finite tree automaton. The type defines a set of values,
|
|
16
16
|
which corresponds to the set of trees accepted by the automaton.
|
|
17
17
|
Therefore, every type admits a unique normal form in terms of its minimal deterministic tree automaton.
|
|
18
18
|
|
|
@@ -38,7 +38,7 @@ Given a finite (possibly empty) set of types `t1, t2, ..., tn` and a set of pair
|
|
|
38
38
|
Each projection function maps a value of type `{t1 l1, t2 l2, ..., tn ln}`
|
|
39
39
|
into a value of type `t1`, `t2`, `...`, `tn`, respectively.
|
|
40
40
|
- by `< t1 l1, t2 l2, ..., tn ln >`, we define a _union type_ with exactly `n` projection functions `/l1`, `/l2`, `...`, `/ln`,
|
|
41
|
-
|
|
41
|
+
mapping a value of type `<t1 l1, t2 l2, ..., tn ln>` into a value of type `t1`, `t2`, `...`, `tn`, respectively.
|
|
42
42
|
|
|
43
43
|
Intuitively, the projection functions on a product type are all total (meaning, they are defined for every value of the
|
|
44
44
|
input product type), whereas in the case of a union type, exactly one projection function is defined
|
|
@@ -68,7 +68,7 @@ Two (possibly recursive) codes are equivalent iff they are bisimilar over their
|
|
|
68
68
|
|
|
69
69
|
### Filters (patterns)
|
|
70
70
|
|
|
71
|
-
A _filter_ is a way of representing a _pattern_, i.e
|
|
71
|
+
A _filter_ is a way of representing a _pattern_, i.e., a set of types sharing some structure.
|
|
72
72
|
For example, if I wanted to refer to all product types that have a field `f` of type `T`,
|
|
73
73
|
I would write: `?{ T f, ... }`. In general, a filter is defined by:
|
|
74
74
|
|
|
@@ -158,14 +158,14 @@ registry of types (schemas) and functions.
|
|
|
158
158
|
|
|
159
159
|
## Typing and Polymorphism (codes and patterns)
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
a dictionary
|
|
161
|
+
The abstract syntax tree of a program consists of a dictionary of _type definitions_,
|
|
162
|
+
a dictionary of _function definitions_, and the final _main expression_.
|
|
163
163
|
|
|
164
164
|
In principle, the initial form of the Abstract Syntax Tree, _raw_ AST, is enough to evaluate
|
|
165
165
|
the program on an input.
|
|
166
166
|
|
|
167
|
-
Our objective however is to compute canonical representations for types and normalized
|
|
168
|
-
representations for functions
|
|
167
|
+
Our objective, however, is to compute canonical representations for types and normalized
|
|
168
|
+
representations for functions so they can be reused.
|
|
169
169
|
|
|
170
170
|
The normalization steps are:
|
|
171
171
|
|
|
@@ -225,7 +225,7 @@ Functions `inc` and `inc3` are (non-polymorphic) functions of types
|
|
|
225
225
|
`${ bit overflown, byte byte } -> ${ bit overflown, byte byte }`, respectively.
|
|
226
226
|
|
|
227
227
|
Function `inc_o` is a polymorphic function of type patterns: `?X -> ${ byte byte, bit overflown }`
|
|
228
|
-
with following constraints:
|
|
228
|
+
with the following constraints:
|
|
229
229
|
|
|
230
230
|
- `?X` is a product type with at least two fields: `byte` and `overflown`, denoted by:
|
|
231
231
|
> `?{ $byte byte, Z overflown, ...}`;
|
|
@@ -269,7 +269,7 @@ The registry will be a key-value store, where the key is the hash of the normali
|
|
|
269
269
|
and the value is the normalized type itself.
|
|
270
270
|
|
|
271
271
|
All functions (non-polymorphic and polymorphic) will be renamed by the hash of their normalized
|
|
272
|
-
definition and stored in
|
|
272
|
+
definition and stored in a similar key-value store.
|
|
273
273
|
|
|
274
274
|
Non-polymorphic functions can be easily indexed by the hashes of their input and output types
|
|
275
275
|
so that we can quickly find the function we need.
|
|
@@ -280,7 +280,7 @@ Indexing polymorphic functions seems complicated (TODO).
|
|
|
280
280
|
The language is designed to transform _codes_, i.e., serialized typed values, i.e., trees
|
|
281
281
|
accepted by a tree automaton, into other _codes_.
|
|
282
282
|
The transformation is done by partial functions as defined in the language.
|
|
283
|
-
The non-recursive and tail-recursive (and even some non-tail
|
|
283
|
+
The non-recursive and tail-recursive (and even some non-tail-recursive) functions can
|
|
284
284
|
be compiled into deterministic finite (pushdown) transducers.
|
|
285
285
|
|
|
286
286
|
### AND-OR graphs for encoding and decoding codes
|
|
@@ -19,6 +19,6 @@ HEAD(?F E) = HEAD(E)
|
|
|
19
19
|
HEAD($T E) = HEAD(E)
|
|
20
20
|
HEAD((E1 E2 ...)) = HEAD(E1)
|
|
21
21
|
HEAD({ E1 f1, E2 f2, ... }) = HEAD(E1) union HEAD(E2) union ...
|
|
22
|
-
HEAD(<E1, E2, ...>) = {} --
|
|
22
|
+
HEAD(<E1, E2, ...>) = {} -- the head of a union is the empty set
|
|
23
23
|
HEAD(/ tag) = HEAD(. lab) = HEAD(| tag) = {}
|
|
24
24
|
```
|
package/DOCS/REPL.md
CHANGED
|
@@ -45,6 +45,9 @@ complete.
|
|
|
45
45
|
| `:C name` | Show canonical code definition |
|
|
46
46
|
| `:codes` | List type aliases |
|
|
47
47
|
| `:rels` | List relation aliases |
|
|
48
|
+
| `:codec load file` | Load an importable REPL codec module |
|
|
49
|
+
| `:codec list` | List loaded REPL codecs |
|
|
50
|
+
| `:input type [codec]` | Read the next line as codec input for a singleton type |
|
|
48
51
|
| `:load [--no-alias] file` | Load `.k` source or `.klib` into the current state |
|
|
49
52
|
| `:klib file` | Export current state as a `.klib` library |
|
|
50
53
|
| `:ko file expr` | Export a `.ko` executable with `expr` as main |
|
|
@@ -133,6 +136,10 @@ Tab completion covers:
|
|
|
133
136
|
|
|
134
137
|
Type aliases also complete in `$name` position inside raw k input.
|
|
135
138
|
|
|
139
|
+
For codec commands, completion covers `:codec load`, `:codec list`, file paths
|
|
140
|
+
after `:codec load`, singleton type names after `:input`, and loaded codec names
|
|
141
|
+
after the input type.
|
|
142
|
+
|
|
136
143
|
## Loading
|
|
137
144
|
|
|
138
145
|
### `:load [--no-alias] file.k`
|
|
@@ -147,6 +154,32 @@ Reads the plain-JSON library file and merges its codes, relations, aliases, and
|
|
|
147
154
|
metadata into the session. Aliases are recovered from `meta[hash].origins[]`
|
|
148
155
|
unless `--no-alias` is used.
|
|
149
156
|
|
|
157
|
+
## Codecs
|
|
158
|
+
|
|
159
|
+
REPL codecs are keyed by canonical singleton code hashes. Open filters are not
|
|
160
|
+
used for codec dispatch.
|
|
161
|
+
|
|
162
|
+
### `:codec load file`
|
|
163
|
+
|
|
164
|
+
Loads an importable ES module that exports a codec shape:
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
export const name = "utf8";
|
|
168
|
+
export const codes = ["@..."];
|
|
169
|
+
export function parse(text) { /* text -> Value */ }
|
|
170
|
+
export function print(value) { /* Value -> text */ }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
A codec may export `patterns` instead of `codes`; each pattern must be a
|
|
174
|
+
singleton closed property-list pattern, and its code hash is recalculated by the
|
|
175
|
+
REPL.
|
|
176
|
+
|
|
177
|
+
### `:input type [codec]`
|
|
178
|
+
|
|
179
|
+
Resolves `type` to a canonical singleton code, selects the registered codec,
|
|
180
|
+
and consumes the next line verbatim as codec input. The parsed value is validated
|
|
181
|
+
against that code before becoming the current value.
|
|
182
|
+
|
|
150
183
|
## Export
|
|
151
184
|
|
|
152
185
|
### `:klib file`
|
package/DOCS/TYPE_DERIVATION.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
This algorithm infers type constraints for programs consisting of mutually recursive partial functions over algebraic data types (products and unions).
|
|
6
6
|
|
|
7
|
-
**Input:** AST of a `k` program, consisting of function definitions (expressions)
|
|
7
|
+
**Input:** AST of a `k` program, consisting of function definitions (expressions), which can be seen as a set of recursive equations.
|
|
8
8
|
**Output:** Annotated AST with type patterns (also called filters)
|
|
9
9
|
|
|
10
10
|
It is assumed that we have access (through APIs) to a universal "type registry", which provides us with unique (canonical) names for each type.
|
|
@@ -22,12 +22,12 @@ A **pattern** represents a set of types:
|
|
|
22
22
|
| `<>` | Closed union | Exactly specified |
|
|
23
23
|
| `T` | Named type | From type definition |
|
|
24
24
|
|
|
25
|
-
**Fields:** Each pattern has associated field labels. For open patterns, additional fields may exist. For closed patterns, the field set is exact.
|
|
25
|
+
**Fields:** Each pattern has associated field labels. For open patterns, additional fields may exist. For closed patterns, the field set is exact.
|
|
26
26
|
Unknown-kind patterns have only the open form `(...)`; a closed filter must choose product `{...}`/`{}` or union `<...>`/`<>`.
|
|
27
27
|
|
|
28
28
|
A type pattern is a singleton pattern, i.e., only one type is captured by such a pattern.
|
|
29
29
|
|
|
30
|
-
## 3. Pattern Graph -
|
|
30
|
+
## 3. Pattern Graph - A Data Structure Used During Type Derivation
|
|
31
31
|
|
|
32
32
|
**Nodes:** Patterns organized in a "reps" (representatives) forest, similar to union-find. However, a union of two (or more) different trees always produces a new "rep" (root) node, joining the trees.
|
|
33
33
|
**Edges:** Labeled by field names, pointing to other patterns
|
|
@@ -42,13 +42,13 @@ A type pattern is a singleton pattern, i.e., only one type is captured by such a
|
|
|
42
42
|
|
|
43
43
|
## 4. Unification
|
|
44
44
|
|
|
45
|
-
`unify(p₁, ..., pₙ)` computes the least upper bound (most specific common pattern); it may add a new node (rep) into the reps forest
|
|
45
|
+
`unify(p₁, ..., pₙ)` computes the least upper bound (most specific common pattern); it may add a new node (rep) into the reps forest, but it does not modify any existing pattern node.
|
|
46
46
|
|
|
47
47
|
**Rules:**
|
|
48
48
|
|
|
49
49
|
- **Open + Open:** Merge field sets, stay open
|
|
50
|
-
- **Open + Closed:** Check
|
|
51
|
-
- **Closed + Closed:** Check
|
|
50
|
+
- **Open + Closed:** Check field inclusion, then become closed
|
|
51
|
+
- **Closed + Closed:** Check field equality, then stay closed
|
|
52
52
|
- **Product ⊥ Union:** Error (incompatible)
|
|
53
53
|
- **Type T:** Unique (canonical name)
|
|
54
54
|
|
|
@@ -75,7 +75,7 @@ out(eᵢ) = in(eᵢ₊₁) for i = 1..n-1
|
|
|
75
75
|
out((e₁ e₂ ... eₙ)) = out(eₙ)
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
When `n=0`, the composition is the identity, `()`.
|
|
79
79
|
|
|
80
80
|
```text
|
|
81
81
|
in(()) = out(())
|
|
@@ -136,7 +136,7 @@ in(T) = out(T) = pattern(T)
|
|
|
136
136
|
see patterns.filterToPattern(F)
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
A filter expression is syntax for describing pattern graphs.
|
|
140
140
|
|
|
141
141
|
## 6. Global Algorithm
|
|
142
142
|
|
|
@@ -166,13 +166,13 @@ For each SCC (in topological order):
|
|
|
166
166
|
For each function `f` in SCC:
|
|
167
167
|
1. Compact `f`'s pattern graph
|
|
168
168
|
2. For each occurrence `o` of `g` in `f`, clone(in(g), out(g)) and unify input and output patterns of `o` with the corresponding cloned patterns
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
If pattern graphs unchanged: break
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
## 7. Compaction
|
|
174
174
|
|
|
175
|
-
**Purpose:** Replace all pattern nodes
|
|
175
|
+
**Purpose:** Replace all pattern nodes with their reps and replace all singleton patterns with types.
|
|
176
176
|
|
|
177
177
|
**Algorithm:**
|
|
178
178
|
|
|
@@ -186,7 +186,7 @@ For each SCC (in topological order):
|
|
|
186
186
|
|
|
187
187
|
**Criterion:** Pattern graph structure unchanged between iterations
|
|
188
188
|
|
|
189
|
-
**Measured by:**
|
|
189
|
+
**Measured by:**
|
|
190
190
|
1. Representative forest structure (parent pointers)
|
|
191
191
|
2. Edge sets for each representative
|
|
192
192
|
3. Pattern descriptors for each representative
|
|
@@ -206,7 +206,7 @@ Converged = (current_state == previous_state)
|
|
|
206
206
|
|
|
207
207
|
## 9. Error Handling
|
|
208
208
|
|
|
209
|
-
**Unification errors:**
|
|
209
|
+
**Unification errors include:**
|
|
210
210
|
|
|
211
211
|
- Product vs Union
|
|
212
212
|
- Closed pattern field mismatch
|
package/DOCS/a.html
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
<h1 id="patterns-and-typed-values">Patterns And Typed Values</h1>
|
|
2
|
+
<h2 id="abstract">Abstract</h2>
|
|
3
|
+
<p>This note proposes a semantic foundation for <code>k</code> patterns,
|
|
4
|
+
types, typed values, and polymorphic values. The central point is that a
|
|
5
|
+
value in <code>k</code> is not a bare tree: it exists only in the
|
|
6
|
+
context of a type. Accordingly, a pattern denotes a set of types, and
|
|
7
|
+
therefore also a set of <strong>typed</strong> values, not a set of raw
|
|
8
|
+
trees. This distinction is essential for reasoning about equality,
|
|
9
|
+
polymorphism, and binary serialization with DAG compression.</p>
|
|
10
|
+
<h2 id="introduction">1. Introduction</h2>
|
|
11
|
+
<p>The <code>k</code> language manipulates algebraic values built from
|
|
12
|
+
products and unions. At first sight one may be tempted to define a value
|
|
13
|
+
simply as a rooted labeled tree and then define types as sets of such
|
|
14
|
+
trees. For <code>k</code>, this is not the right semantic level.</p>
|
|
15
|
+
<p>Two trees with the same shape but belonging to different types are
|
|
16
|
+
different values. Type is part of value identity.</p>
|
|
17
|
+
<p>This leads to the following semantic ladder:</p>
|
|
18
|
+
<ul>
|
|
19
|
+
<li>a <strong>type</strong> denotes a set of typed values,</li>
|
|
20
|
+
<li>a <strong>pattern</strong> denotes a set of types,</li>
|
|
21
|
+
<li>therefore a <strong>pattern</strong> also denotes a set of typed
|
|
22
|
+
values,</li>
|
|
23
|
+
<li>a <strong>polymorphic value</strong> is a pattern together with a
|
|
24
|
+
typed value compatible with that pattern.</li>
|
|
25
|
+
</ul>
|
|
26
|
+
<p>The rest of this note makes these notions precise.</p>
|
|
27
|
+
<h2 id="types">2. Types</h2>
|
|
28
|
+
<p>A <strong>type graph</strong> is a finite rooted directed graph with
|
|
29
|
+
labeled edges. Each node is one of:</p>
|
|
30
|
+
<ul>
|
|
31
|
+
<li><code>product</code>,</li>
|
|
32
|
+
<li><code>union</code>.</li>
|
|
33
|
+
</ul>
|
|
34
|
+
<p>For a product node, outgoing edge labels are field names. For a union
|
|
35
|
+
node, outgoing edge labels are tags. Edges are locally unique by
|
|
36
|
+
label.</p>
|
|
37
|
+
<p>The graph may be cyclic, which allows recursive types.</p>
|
|
38
|
+
<p>Two type graphs that are equivalent as regular tree languages may be
|
|
39
|
+
identified. For types, minimization by bisimulation is appropriate.</p>
|
|
40
|
+
<h3 id="trees-accepted-by-a-type">2.1. Trees accepted by a type</h3>
|
|
41
|
+
<p>Let <code>Tree(T)</code> denote the set of rooted value trees
|
|
42
|
+
accepted by a type graph <code>T</code>. Acceptance is defined in the
|
|
43
|
+
standard way:</p>
|
|
44
|
+
<ul>
|
|
45
|
+
<li>a product tree is accepted by a product node when its fields match
|
|
46
|
+
the outgoing labels of that node and each child tree is accepted by the
|
|
47
|
+
corresponding target node,</li>
|
|
48
|
+
<li>a union tree is accepted by a union node when it chooses exactly one
|
|
49
|
+
outgoing tag and the child tree is accepted by the corresponding target
|
|
50
|
+
node.</li>
|
|
51
|
+
</ul>
|
|
52
|
+
<h2 id="typed-values">3. Typed Values</h2>
|
|
53
|
+
<p>A <strong>typed value</strong> is a pair</p>
|
|
54
|
+
<pre class="text"><code>(T, t)</code></pre>
|
|
55
|
+
<p>where:</p>
|
|
56
|
+
<ul>
|
|
57
|
+
<li><code>T</code> is a type graph,</li>
|
|
58
|
+
<li><code>t ∈ Tree(T)</code>.</li>
|
|
59
|
+
</ul>
|
|
60
|
+
<p>Thus a value is intrinsically typed. The tree <code>t</code> is only
|
|
61
|
+
a representation of the value; it is not, by itself, a semantic value of
|
|
62
|
+
the language.</p>
|
|
63
|
+
<p>We write:</p>
|
|
64
|
+
<pre class="text"><code>Val(T) = { (T, t) : t ∈ Tree(T) }.</code></pre>
|
|
65
|
+
<h3 id="identity-of-typed-values">3.1. Identity of typed values</h3>
|
|
66
|
+
<p>Typed values are intensional with respect to the type:</p>
|
|
67
|
+
<pre class="text"><code>(T, t) ≠ (U, t) whenever T ≠ U.</code></pre>
|
|
68
|
+
<p>Even when the same raw tree <code>t</code> is accepted by both
|
|
69
|
+
<code>T</code> and <code>U</code>, the two typed values are
|
|
70
|
+
different.</p>
|
|
71
|
+
<p>This point is essential for serialization: the identity of a value
|
|
72
|
+
cannot be recovered from the raw tree alone.</p>
|
|
73
|
+
<h2 id="patterns">4. Patterns</h2>
|
|
74
|
+
<p>A <strong>pattern graph</strong> is a finite rooted directed graph
|
|
75
|
+
with labeled edges. Each node carries:</p>
|
|
76
|
+
<ul>
|
|
77
|
+
<li>a node kind:
|
|
78
|
+
<ul>
|
|
79
|
+
<li><code>unknown</code>,</li>
|
|
80
|
+
<li><code>product</code>,</li>
|
|
81
|
+
<li><code>union</code>,</li>
|
|
82
|
+
</ul></li>
|
|
83
|
+
<li>an openness flag:
|
|
84
|
+
<ul>
|
|
85
|
+
<li><code>open</code>,</li>
|
|
86
|
+
<li><code>closed</code>.</li>
|
|
87
|
+
</ul></li>
|
|
88
|
+
</ul>
|
|
89
|
+
<p>For product and union nodes, outgoing edges are labeled by field
|
|
90
|
+
names or tags. Edges are locally unique by label.</p>
|
|
91
|
+
<p>Unlike the case of types, <strong>node identity in a pattern graph is
|
|
92
|
+
semantic</strong>. If two edges point to the same pattern node, this
|
|
93
|
+
means that the corresponding positions must be inhabited by the very
|
|
94
|
+
same type.</p>
|
|
95
|
+
<p>Therefore patterns are not quotiented by bisimulation in general.</p>
|
|
96
|
+
<h2 id="filters-as-syntax">5. Filters As Syntax</h2>
|
|
97
|
+
<p>A filter expression is a textual way of describing a pattern
|
|
98
|
+
graph.</p>
|
|
99
|
+
<p>Variables appearing in filters are not the semantic object itself.
|
|
100
|
+
They are a device for serializing and reusing pattern graph nodes in
|
|
101
|
+
syntax.</p>
|
|
102
|
+
<p>For example, the two filters</p>
|
|
103
|
+
<pre class="k"><code>{ (...) x, (...) y, ... }
|
|
104
|
+
{ X x, X y, ... }</code></pre>
|
|
105
|
+
<p>describe different pattern graphs:</p>
|
|
106
|
+
<ul>
|
|
107
|
+
<li>the first has two distinct target nodes for <code>x</code> and
|
|
108
|
+
<code>y</code>,</li>
|
|
109
|
+
<li>the second has one shared target node reached from both
|
|
110
|
+
<code>x</code> and <code>y</code>.</li>
|
|
111
|
+
</ul>
|
|
112
|
+
<p>These two patterns are semantically different.</p>
|
|
113
|
+
<h2 id="matching-a-type-against-a-pattern">6. Matching A Type Against A
|
|
114
|
+
Pattern</h2>
|
|
115
|
+
<p>Let <code>P</code> be a pattern graph and <code>T</code> a type
|
|
116
|
+
graph. We say that <code>T</code> <strong>matches</strong>
|
|
117
|
+
<code>P</code>, written</p>
|
|
118
|
+
<pre class="text"><code>T ⊨ P,</code></pre>
|
|
119
|
+
<p>if there exists a root-preserving graph morphism</p>
|
|
120
|
+
<pre class="text"><code>h : nodes(P) → nodes(T)</code></pre>
|
|
121
|
+
<p>such that:</p>
|
|
122
|
+
<ol type="1">
|
|
123
|
+
<li><p>if <code>p</code> is <code>unknown</code>, then <code>h(p)</code>
|
|
124
|
+
may be any type node,</p></li>
|
|
125
|
+
<li><p>if <code>p</code> is <code>product</code>, then <code>h(p)</code>
|
|
126
|
+
is a product node,</p></li>
|
|
127
|
+
<li><p>if <code>p</code> is <code>union</code>, then <code>h(p)</code>
|
|
128
|
+
is a union node,</p></li>
|
|
129
|
+
<li><p>if <code>p</code> is <code>closed</code>, then the outgoing
|
|
130
|
+
labels of <code>h(p)</code> are exactly the outgoing labels of
|
|
131
|
+
<code>p</code>,</p></li>
|
|
132
|
+
<li><p>if <code>p</code> is <code>open</code>, then the outgoing labels
|
|
133
|
+
of <code>h(p)</code> contain at least the outgoing labels of
|
|
134
|
+
<code>p</code>,</p></li>
|
|
135
|
+
<li><p>for every pattern edge</p>
|
|
136
|
+
<pre class="text"><code>p --l--> q</code></pre>
|
|
137
|
+
<p>there is a corresponding type edge</p>
|
|
138
|
+
<pre class="text"><code>h(p) --l--> h(q).</code></pre></li>
|
|
139
|
+
</ol>
|
|
140
|
+
<p>Condition 6 is the crucial one: if two edges of <code>P</code> point
|
|
141
|
+
to the same node <code>q</code>, then the corresponding edges of
|
|
142
|
+
<code>T</code> must point to the same type node <code>h(q)</code>. This
|
|
143
|
+
is how the graph structure of the pattern expresses “the same type”.</p>
|
|
144
|
+
<p>We define:</p>
|
|
145
|
+
<pre class="text"><code>Types(P) = { T : T ⊨ P }.</code></pre>
|
|
146
|
+
<h3 id="proposition-0">Proposition 0</h3>
|
|
147
|
+
<p>If <code>T ⊨ P</code>, then:</p>
|
|
148
|
+
<ul>
|
|
149
|
+
<li>the root kind of <code>T</code> is compatible with the root kind of
|
|
150
|
+
<code>P</code>,</li>
|
|
151
|
+
<li>every label explicitly present at the root of <code>P</code> is
|
|
152
|
+
present at the root of <code>T</code>,</li>
|
|
153
|
+
<li>if the root of <code>P</code> is closed, then <code>T</code> has no
|
|
154
|
+
additional root labels.</li>
|
|
155
|
+
</ul>
|
|
156
|
+
<h4 id="proof">Proof</h4>
|
|
157
|
+
<p>All three statements are immediate from the definition of the
|
|
158
|
+
root-preserving morphism <code>h</code> and conditions 1-5 above,
|
|
159
|
+
applied at the root node of <code>P</code>.</p>
|
|
160
|
+
<h2 id="singleton-patterns-and-types">7. Singleton Patterns And
|
|
161
|
+
Types</h2>
|
|
162
|
+
<p>Every type graph <code>T</code> determines a closed pattern
|
|
163
|
+
<code>Pat(T)</code> obtained by forgetting that it is a type and viewing
|
|
164
|
+
it as a closed pattern graph.</p>
|
|
165
|
+
<p>This gives the following proposition.</p>
|
|
166
|
+
<h3 id="proposition-1">Proposition 1</h3>
|
|
167
|
+
<p>For every type <code>T</code>, the pattern <code>Pat(T)</code> is
|
|
168
|
+
singleton:</p>
|
|
169
|
+
<pre class="text"><code>Types(Pat(T)) = { T }</code></pre>
|
|
170
|
+
<p>up to the chosen notion of type equivalence.</p>
|
|
171
|
+
<h4 id="proof-1">Proof</h4>
|
|
172
|
+
<p>The identity map from <code>nodes(Pat(T))</code> to
|
|
173
|
+
<code>nodes(T)</code> witnesses <code>T ⊨ Pat(T)</code>. Conversely,
|
|
174
|
+
suppose <code>U ⊨ Pat(T)</code> via a root-preserving morphism
|
|
175
|
+
<code>h</code>. Because every node of <code>Pat(T)</code> is either
|
|
176
|
+
product or union, every node kind is preserved. Because every node is
|
|
177
|
+
closed, the outgoing labels at each image node are exactly the outgoing
|
|
178
|
+
labels of the corresponding node of <code>T</code>. Because every
|
|
179
|
+
pattern edge must be respected by <code>h</code>, all target-sharing
|
|
180
|
+
constraints are preserved as well. Therefore <code>h</code> is a graph
|
|
181
|
+
isomorphism modulo the chosen equivalence on type graphs, and hence
|
|
182
|
+
<code>U</code> is equivalent to <code>T</code>.</p>
|
|
183
|
+
<h3 id="proposition-2">Proposition 2</h3>
|
|
184
|
+
<p>Conversely, every singleton pattern determines a unique type up to
|
|
185
|
+
type equivalence.</p>
|
|
186
|
+
<h4 id="proof-2">Proof</h4>
|
|
187
|
+
<p>Let <code>P</code> be singleton. By definition, <code>Types(P)</code>
|
|
188
|
+
contains exactly one type up to equivalence. Choose any representative
|
|
189
|
+
<code>T ∈ Types(P)</code>. Then <code>T</code> is the unique type
|
|
190
|
+
determined by <code>P</code>, again up to the chosen equivalence
|
|
191
|
+
relation.</p>
|
|
192
|
+
<p>Thus types can be regarded as a special case of patterns.</p>
|
|
193
|
+
<h2 id="polymorphic-values">8. Polymorphic Values</h2>
|
|
194
|
+
<p>A <strong>polymorphic value</strong> is a pair</p>
|
|
195
|
+
<pre class="text"><code>(P, v)</code></pre>
|
|
196
|
+
<p>where:</p>
|
|
197
|
+
<ul>
|
|
198
|
+
<li><code>P</code> is a pattern graph,</li>
|
|
199
|
+
<li><code>v</code> is a typed value <code>(T, t)</code>,</li>
|
|
200
|
+
<li><code>T ⊨ P</code>.</li>
|
|
201
|
+
</ul>
|
|
202
|
+
<p>Equivalently:</p>
|
|
203
|
+
<pre class="text"><code>PolyVal(P) = { (P, (T, t)) : T ⊨ P and t ∈ Tree(T) }.</code></pre>
|
|
204
|
+
<p>So a pattern does not denote a set of raw trees. It denotes a set of
|
|
205
|
+
typed values:</p>
|
|
206
|
+
<pre class="text"><code>Val(P) = { (T, t) : T ⊨ P and t ∈ Tree(T) }.</code></pre>
|
|
207
|
+
<p>This is a disjoint union over matching types, not an ordinary union
|
|
208
|
+
of raw tree sets.</p>
|
|
209
|
+
<h3 id="proposition-3">Proposition 3</h3>
|
|
210
|
+
<p>For every type <code>T</code>,</p>
|
|
211
|
+
<pre class="text"><code>Val(Pat(T)) = Val(T).</code></pre>
|
|
212
|
+
<h4 id="proof-3">Proof</h4>
|
|
213
|
+
<p>By Proposition 1, <code>Types(Pat(T)) = { T }</code> up to
|
|
214
|
+
equivalence. Substituting this into the definition of
|
|
215
|
+
<code>Val(P)</code> yields exactly the set of typed values whose type is
|
|
216
|
+
<code>T</code>.</p>
|
|
217
|
+
<h2 id="examples">9. Examples</h2>
|
|
218
|
+
<h3 id="distinct-pattern-graphs">9.1. Distinct pattern graphs</h3>
|
|
219
|
+
<p>Consider:</p>
|
|
220
|
+
<pre class="k"><code>P1 = { (...) x, (...) y, ... }
|
|
221
|
+
P2 = { X x, X y, ... }</code></pre>
|
|
222
|
+
<p>Then <code>P1</code> and <code>P2</code> are different patterns.</p>
|
|
223
|
+
<ul>
|
|
224
|
+
<li><code>P1</code> allows the types at <code>x</code> and
|
|
225
|
+
<code>y</code> to differ.</li>
|
|
226
|
+
<li><code>P2</code> requires the types at <code>x</code> and
|
|
227
|
+
<code>y</code> to be the same.</li>
|
|
228
|
+
</ul>
|
|
229
|
+
<p>Hence:</p>
|
|
230
|
+
<pre class="text"><code>Types(P2) ⊊ Types(P1).</code></pre>
|
|
231
|
+
<h4 id="proof-sketch">Proof sketch</h4>
|
|
232
|
+
<p>Every type matching <code>P2</code> also matches <code>P1</code>,
|
|
233
|
+
because identifying the target types of <code>x</code> and
|
|
234
|
+
<code>y</code> is stronger than leaving them unrelated. The inclusion is
|
|
235
|
+
strict because a product type with distinct types at <code>x</code> and
|
|
236
|
+
<code>y</code> matches <code>P1</code> but not <code>P2</code>.</p>
|
|
237
|
+
<h3 id="same-tree-different-typed-values">9.2. Same tree, different
|
|
238
|
+
typed values</h3>
|
|
239
|
+
<p>Let <code>t = {}|x</code>. Suppose <code>t</code> is accepted both
|
|
240
|
+
by</p>
|
|
241
|
+
<pre class="k"><code>T1 = < {} x >
|
|
242
|
+
T2 = < {} x, {} y ></code></pre>
|
|
243
|
+
<p>Then <code>(T1, t)</code> and <code>(T2, t)</code> are different
|
|
244
|
+
typed values.</p>
|
|
245
|
+
<p>The raw tree is the same, but the type is different.</p>
|
|
246
|
+
<h3 id="proposition-4">Proposition 4</h3>
|
|
247
|
+
<p>If <code>T ≠ U</code>, then:</p>
|
|
248
|
+
<pre class="text"><code>Val(T) ∩ Val(U) = ∅.</code></pre>
|
|
249
|
+
<h4 id="proof-4">Proof</h4>
|
|
250
|
+
<p>An element of <code>Val(T)</code> has the form <code>(T, t)</code>.
|
|
251
|
+
An element of <code>Val(U)</code> has the form <code>(U, u)</code>. If
|
|
252
|
+
<code>(T, t) = (U, u)</code>, then necessarily <code>T = U</code>.
|
|
253
|
+
Hence, when <code>T ≠ U</code>, the two sets are disjoint.</p>
|
|
254
|
+
<h2 id="consequences-for-equality-and-compression">10. Consequences For
|
|
255
|
+
Equality And Compression</h2>
|
|
256
|
+
<p>Since values are intrinsically typed, raw tree equality is not
|
|
257
|
+
sufficient for semantic equality.</p>
|
|
258
|
+
<p>This has an immediate consequence for DAG compression. Suppose we
|
|
259
|
+
have the raw tree</p>
|
|
260
|
+
<pre class="k"><code>{{}|x a, {}|x b, {}|x c}</code></pre>
|
|
261
|
+
<p>and consider two patterns:</p>
|
|
262
|
+
<pre class="k"><code>P1 = { (...) a, (...) b, (...) c, ... }
|
|
263
|
+
P2 = { X a, X b, X c, ... }</code></pre>
|
|
264
|
+
<p>If only the raw tree is considered, the three occurrences of
|
|
265
|
+
<code>{}|x</code> appear identical and a naive compressor may merge
|
|
266
|
+
them. But semantically this is not always justified.</p>
|
|
267
|
+
<ul>
|
|
268
|
+
<li>Under <code>P1</code>, the three occurrences need not belong to the
|
|
269
|
+
same type.</li>
|
|
270
|
+
<li>Under <code>P2</code>, they must belong to the same type.</li>
|
|
271
|
+
</ul>
|
|
272
|
+
<p>Therefore canonical value sharing, when used as an optimization, must
|
|
273
|
+
be relative to the typing information supplied by the pattern and the
|
|
274
|
+
witness type. One must not collapse raw subtrees merely because they
|
|
275
|
+
look the same as trees.</p>
|
|
276
|
+
<h3 id="proposition-5">Proposition 5</h3>
|
|
277
|
+
<p>There is, in general, no canonical DAG compression procedure that
|
|
278
|
+
depends only on the raw value tree and is correct for all polymorphic
|
|
279
|
+
values.</p>
|
|
280
|
+
<h4 id="proof-5">Proof</h4>
|
|
281
|
+
<p>Consider the raw tree</p>
|
|
282
|
+
<pre class="text"><code>{{}|x a, {}|x b, {}|x c}.</code></pre>
|
|
283
|
+
<p>As a raw tree, the three subtrees under <code>a</code>,
|
|
284
|
+
<code>b</code>, and <code>c</code> are isomorphic. Hence any tree-only
|
|
285
|
+
compressor cannot distinguish their occurrences by local tree structure
|
|
286
|
+
alone. Now compare the two valid pattern contexts:</p>
|
|
287
|
+
<pre class="text"><code>P1 = { (...) a, (...) b, (...) c, ... }
|
|
288
|
+
P2 = { X a, X b, X c, ... }.</code></pre>
|
|
289
|
+
<p>Under <code>P1</code>, the three occurrences are not forced to have
|
|
290
|
+
the same type. Under <code>P2</code>, they are forced to have the same
|
|
291
|
+
type because all three edges land in the same pattern node. So a
|
|
292
|
+
compressor that always merges these subtrees is unsound for
|
|
293
|
+
<code>P1</code>, while a compressor that never merges them fails to
|
|
294
|
+
capture the canonical sharing justified by <code>P2</code>. Therefore no
|
|
295
|
+
canonical sharing rule can be defined from the raw tree alone; typing
|
|
296
|
+
information from the pattern and witness type is required.</p>
|
|
297
|
+
<h3 id="proposition-6">Proposition 6</h3>
|
|
298
|
+
<p>For a fixed polymorphic value <code>(P, (T, t))</code>, any canonical
|
|
299
|
+
DAG compression of <code>t</code> must be invariant under equality of
|
|
300
|
+
<strong>typed occurrences</strong>, not merely equality of raw
|
|
301
|
+
subtrees.</p>
|
|
302
|
+
<h4 id="proof-sketch-1">Proof sketch</h4>
|
|
303
|
+
<p>By Proposition 4, typed values of different types are distinct even
|
|
304
|
+
when represented by the same raw tree. Within a fixed witness type
|
|
305
|
+
<code>T</code>, the pattern graph still matters because distinct
|
|
306
|
+
positions of <code>P</code> may or may not be identified. Therefore the
|
|
307
|
+
only semantically defensible sharing criterion is one that respects the
|
|
308
|
+
typing/decorated occurrence information carried by <code>(P, T)</code>,
|
|
309
|
+
not the undecorated tree <code>t</code> alone.</p>
|
|
310
|
+
<h2 id="topological-remark">11. Topological Remark</h2>
|
|
311
|
+
<p>There is a topology-like intuition behind patterns, but only for a
|
|
312
|
+
fragment.</p>
|
|
313
|
+
<p>If one looks only at open positive patterns, they behave like
|
|
314
|
+
observable structural properties of types and suggest a topology or,
|
|
315
|
+
more precisely, an Alexandrov-style structure on the class of types
|
|
316
|
+
ordered by refinement.</p>
|
|
317
|
+
<p>However, the full pattern formalism is richer than topology because
|
|
318
|
+
it includes:</p>
|
|
319
|
+
<ul>
|
|
320
|
+
<li>closedness constraints,</li>
|
|
321
|
+
<li>explicit graph sharing, which expresses sameness of type at
|
|
322
|
+
different positions.</li>
|
|
323
|
+
</ul>
|
|
324
|
+
<p>These two features go beyond ordinary open-set semantics.</p>
|
|
325
|
+
<h3 id="proposition-7">Proposition 7</h3>
|
|
326
|
+
<p>The class of patterns is strictly richer than the open positive
|
|
327
|
+
fragment.</p>
|
|
328
|
+
<h4 id="proof-sketch-2">Proof sketch</h4>
|
|
329
|
+
<p>The open positive fragment can describe only monotone structural
|
|
330
|
+
requirements such as “at least these labels” and “these positions have
|
|
331
|
+
these subpatterns”. It cannot express exact closedness, because
|
|
332
|
+
closedness is not monotone under refinement. It also cannot express
|
|
333
|
+
identification of two positions as “the same type” without preserving
|
|
334
|
+
explicit graph sharing. Since full patterns include both closedness and
|
|
335
|
+
shared-node identity, they strictly extend the open positive
|
|
336
|
+
fragment.</p>
|
|
337
|
+
<h2 id="summary">12. Summary</h2>
|
|
338
|
+
<ul>
|
|
339
|
+
<li>A type is a regular rooted graph denoting a set of accepted
|
|
340
|
+
trees.</li>
|
|
341
|
+
<li>A typed value is a pair <code>(T, t)</code> with
|
|
342
|
+
<code>t ∈ Tree(T)</code>.</li>
|
|
343
|
+
<li>A pattern is a rooted graph with node kind, openness, and semantic
|
|
344
|
+
node identity.</li>
|
|
345
|
+
<li>A filter is only syntax for serializing a pattern graph.</li>
|
|
346
|
+
<li>A type matches a pattern via a root-preserving morphism respecting
|
|
347
|
+
node kind, openness, labels, and shared targets.</li>
|
|
348
|
+
<li>A pattern denotes a set of types, and therefore a set of typed
|
|
349
|
+
values, not a set of raw trees.</li>
|
|
350
|
+
<li>A polymorphic value is a pair <code>(P, (T, t))</code> with
|
|
351
|
+
<code>T ⊨ P</code>.</li>
|
|
352
|
+
<li>Types are exactly the singleton-pattern case.</li>
|
|
353
|
+
<li>DAG compression of values is a representation optimization only and
|
|
354
|
+
must respect typing information.</li>
|
|
355
|
+
</ul>
|
|
356
|
+
<h2 id="corollaries-for-binary-serialization">13. Corollaries For Binary
|
|
357
|
+
Serialization</h2>
|
|
358
|
+
<p>The previous propositions impose strong constraints on any correct
|
|
359
|
+
binary format.</p>
|
|
360
|
+
<h3 id="corollary-1">Corollary 1</h3>
|
|
361
|
+
<p>The semantic payload of a serialized polymorphic value must
|
|
362
|
+
determine:</p>
|
|
363
|
+
<ul>
|
|
364
|
+
<li>a pattern graph <code>P</code>,</li>
|
|
365
|
+
<li>a witness typed value <code>(T, t)</code>,</li>
|
|
366
|
+
</ul>
|
|
367
|
+
<p>with <code>T ⊨ P</code>.</p>
|
|
368
|
+
<p>It is not sufficient to serialize only a raw tree.</p>
|
|
369
|
+
<h4 id="justification">Justification</h4>
|
|
370
|
+
<p>By Proposition 4, type is part of value identity. By Proposition 5,
|
|
371
|
+
the raw tree alone is insufficient to determine correct canonical
|
|
372
|
+
sharing behavior.</p>
|
|
373
|
+
<h3 id="corollary-2">Corollary 2</h3>
|
|
374
|
+
<p>A closed monomorphic typed value is the special case where the
|
|
375
|
+
serialized pattern is singleton.</p>
|
|
376
|
+
<h4 id="justification-1">Justification</h4>
|
|
377
|
+
<p>This is Proposition 1 together with Proposition 3.</p>
|
|
378
|
+
<h3 id="corollary-3">Corollary 3</h3>
|
|
379
|
+
<p>Any canonical DAG representation of the witness value must be
|
|
380
|
+
computed from the decorated object <code>(P, T, t)</code>, not from
|
|
381
|
+
<code>t</code> alone.</p>
|
|
382
|
+
<h4 id="justification-2">Justification</h4>
|
|
383
|
+
<p>This is exactly the content of Propositions 5 and 6.</p>
|
|
384
|
+
<h3 id="corollary-4">Corollary 4</h3>
|
|
385
|
+
<p>Pattern serialization must preserve explicit pattern-node
|
|
386
|
+
identity.</p>
|
|
387
|
+
<h4 id="justification-3">Justification</h4>
|
|
388
|
+
<p>Pattern node sharing expresses sameness of type at different
|
|
389
|
+
positions. Replacing a pattern graph by any representation that forgets
|
|
390
|
+
this identity changes its denotation.</p>
|
|
391
|
+
<h3 id="corollary-5">Corollary 5</h3>
|
|
392
|
+
<p>For singleton patterns, one may replace the explicit serialized
|
|
393
|
+
pattern graph by a canonical type reference as a compact optimization,
|
|
394
|
+
provided that the decoding semantics is unchanged.</p>
|
|
395
|
+
<h4 id="justification-4">Justification</h4>
|
|
396
|
+
<p>By Proposition 2, a singleton pattern determines a unique type up to
|
|
397
|
+
equivalence. Therefore the explicit graph may be recoverable from a
|
|
398
|
+
canonical type identifier.</p>
|