@fraczak/k 3.1.0 → 4.0.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/typing.mjs CHANGED
@@ -183,6 +183,134 @@ class TypePatternGraph {
183
183
  return gNodes.filter(x => !((x in excluded) || (this.patterns.nodes[x].pattern == 'type')));
184
184
  }
185
185
 
186
+ getCompressed() {
187
+ const newTypePatternGraph = new TypePatternGraph();
188
+ const renamed = this.cloneAll(newTypePatternGraph);
189
+ newTypePatternGraph.turnSingletonPatternsIntoCodes();
190
+
191
+ Object.keys(renamed).forEach( id => {
192
+ renamed[id] = newTypePatternGraph.find(renamed[id]);
193
+ });
194
+
195
+ // console.log("newTypePatternGraph", newTypePatternGraph);
196
+ // console.log("nodes", newTypePatternGraph.patterns.nodes);
197
+ // console.log("edges", newTypePatternGraph.edges);
198
+
199
+ const rootPatternIds = newTypePatternGraph.patterns.nodes.map((x,i) => i)
200
+ .filter(x => newTypePatternGraph.find(x) == x);
201
+
202
+ // console.log("rootPatternIds", rootPatternIds);
203
+
204
+ const equivalence = rootPatternIds.reduce( (equivalence,i) => {
205
+ const pattern = newTypePatternGraph.get_pattern(i);
206
+ switch (pattern.pattern) {
207
+ case 'type':
208
+ case '(...)':
209
+ case '{...}':
210
+ case '<...>':
211
+ equivalence.push([i]);
212
+ break;
213
+ default:
214
+ equivalence[0].push(i);
215
+ };
216
+ return equivalence;
217
+ }, [[]]).filter(x => x.length > 0);
218
+
219
+ const reps = equivalence.reduce( (reps,eqClass) => {
220
+ eqClass.forEach( x => reps[x] = eqClass);
221
+ return reps;
222
+ }, {});
223
+
224
+ // console.log("equivalence", equivalence);
225
+
226
+ const areDifferent = (i1,i2) => {
227
+ const p1 = newTypePatternGraph.get_pattern(i1);
228
+ const p2 = newTypePatternGraph.get_pattern(i2);
229
+ if (p1.pattern != p2.pattern) return true;
230
+ const e1 = newTypePatternGraph.edges[i1];
231
+ const e2 = newTypePatternGraph.edges[i2];
232
+ if (Object.keys(e1).length != Object.keys(e2).length) return true;
233
+ for (const lab in e1) {
234
+ if (e2[lab] == undefined) return true;
235
+ const dests1 = Object.values(e1[lab])[0];
236
+ const dests2 = Object.values(e2[lab])[0];
237
+ if (reps[dests1] != reps[dests2]) return true;
238
+ }
239
+ return false;
240
+ }
241
+ let changed = true;
242
+ while (changed) {
243
+ changed = false;
244
+ const equivalenceSize = equivalence.length;
245
+ for (let eqClassPos = 0; eqClassPos < equivalenceSize; eqClassPos++) {
246
+ const eqClass = equivalence[eqClassPos];
247
+ const oldClass = [eqClass[0]];
248
+ const newClass = [];
249
+ for (let i = 1; i < eqClass.length; i++) {
250
+ if (areDifferent(oldClass[0],eqClass[i])) {
251
+ newClass.push(eqClass[i]);
252
+ } else {
253
+ oldClass.push(eqClass[i]);
254
+ }
255
+ }
256
+ if (newClass.length > 0) {
257
+ changed = true;
258
+ equivalence.push(newClass);
259
+ equivalence[eqClassPos] = oldClass;
260
+ for (const i of newClass) {
261
+ reps[i] = newClass;
262
+ }
263
+ for (const i of oldClass) {
264
+ reps[i] = oldClass;
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+ // console.log("equivalence (final)", equivalence);
271
+ // console.log("reps", reps);
272
+
273
+ const compressedTypePatternGraph = new TypePatternGraph();
274
+ const newIds = {};
275
+ for (const eqClass of equivalence) {
276
+ // console.log("eqClass", eqClass);
277
+ const oldId = eqClass[0];
278
+ const pattern = newTypePatternGraph.get_pattern(oldId);
279
+ const newId = (() => {
280
+ if (pattern.pattern == 'type') return compressedTypePatternGraph.getTypeId(pattern.type);
281
+ return compressedTypePatternGraph.patterns.addNewNode(pattern);
282
+ })();
283
+ newIds[oldId] = newId;
284
+ }
285
+ for (const oldId in newIds) {
286
+ const newId = newIds[oldId];
287
+ const pattern = newTypePatternGraph.get_pattern(oldId);
288
+ const newPattern = compressedTypePatternGraph.get_pattern(newId);
289
+ // console.log("patterns", pattern, newPattern);
290
+ if (pattern.pattern == 'type')
291
+ continue;
292
+ const edges = newTypePatternGraph.edges[oldId];
293
+ // console.log("edges", edges);
294
+ compressedTypePatternGraph.edges[newId] = {};
295
+ for (const lab in edges) {
296
+ // console.log("edges[lab]", edges[lab]);
297
+ compressedTypePatternGraph.edges[newId][lab] = asMapSet(Object.values(edges[lab]).map(x =>
298
+ newIds[reps[newTypePatternGraph.find(x)][0]]));
299
+ }
300
+ }
301
+
302
+ const remapping = Object.keys(renamed).reduce( (remapping,id) => {
303
+ return {...remapping, [id]: newIds[ reps[renamed[id]][0] ]};
304
+ }, {});
305
+
306
+ // console.log("remapping", remapping);
307
+ // console.log("compressedTypePatternGraph", compressedTypePatternGraph);
308
+ // console.log("nodes", compressedTypePatternGraph.patterns.nodes);
309
+ // console.log("edges", compressedTypePatternGraph.edges);
310
+ // console.log("--------------------")
311
+ return {typePatternGraph: compressedTypePatternGraph, remapping};
312
+ }
313
+
186
314
  clone(roots,targetGraph = this) {
187
315
  const result = {}; // mapping from original ids to cloned ids
188
316
  const find = (x) => this.find(x);
@@ -240,7 +368,8 @@ class TypePatternGraph {
240
368
  if (subsetP(p1.fields, p2.fields)) return p2;
241
369
  throw new Error(`Cannot unify (${p1.fields},...) with (${p2.fields})`);
242
370
  case '[]':
243
- return p2;
371
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/))) return p2;
372
+ throw new Error(`Cannot unify [] with (${p1.fields},...)`);
244
373
  case '{}':
245
374
  if (subsetP(p1.fields, p2.fields)) return p2;
246
375
  throw new Error(`Cannot unify (${p1.fields},...) with {${p2.fields}}`);
@@ -283,7 +412,8 @@ class TypePatternGraph {
283
412
  if (subsetP(p1.fields, p2.fields))
284
413
  return {pattern: '{}', fields: p2.fields};
285
414
  case '[]':
286
- return p2;
415
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/))) return p2;
416
+ throw new Error(`Cannot unify [] with {${p1.fields},...}`);
287
417
  case '{}':
288
418
  if (subsetP(p1.fields, p2.fields)) return p2;
289
419
  throw new Error(`Cannot unify {${p1.fields},...} with {${p2.fields}}`);
@@ -319,6 +449,17 @@ class TypePatternGraph {
319
449
  case 'type':{
320
450
  const code = codes.find(p2.type);
321
451
  switch (code.code) {
452
+ case 'int':
453
+ // supports (2 .2) which returns unit
454
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/)))
455
+ return p2;
456
+ break;
457
+ case 'string':
458
+ return p2;
459
+ case 'bool':
460
+ if (p1.fields.every(x => `${x}`.match(/^true|false$/)))
461
+ return p2;
462
+ break;
322
463
  case 'union': {
323
464
  let p2_fields = Object.keys(code[code.code]);
324
465
  if (subsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
@@ -328,7 +469,7 @@ class TypePatternGraph {
328
469
  };
329
470
  };
330
471
  break;
331
- case '()':
472
+ case '()': // Pattern '()' can be introduced by filter e.g., ?(X x, Y x)
332
473
  switch (p2.pattern) {
333
474
  case '()':
334
475
  if (eqsetP(p1.fields, p2.fields)) return p2;
@@ -337,18 +478,32 @@ class TypePatternGraph {
337
478
  if (eqsetP(p1.fields, p2.fields)) return p2;
338
479
  throw new Error(`Cannot unify (${p1.fields}) with {${p2.fields}}`);
339
480
  case '[]':
340
- return p2;
481
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/))) return p2;
482
+ throw new Error(`Cannot unify [] with (${p1.fields})`);
341
483
  case '<>':
342
484
  if (eqsetP(p1.fields, p2.fields)) return p2;
343
485
  throw new Error(`Cannot unify (${p1.fields}) with <${p2.fields}>`);
344
486
  case 'type':{
345
487
  const code = codes.find(p2.type);
346
488
  switch (code.code) {
489
+ case 'int':
490
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/)))
491
+ return p2;
492
+ break;
493
+ case 'string':
494
+ return p2;
495
+ case 'bool':
496
+ if (p1.fields.every(x => `${x}`.match(/^true|false$/)))
497
+ return p2;
498
+ break;
347
499
  case 'product':
348
500
  case 'union': {
349
501
  let p2_fields = Object.keys(code[code.code]);
350
502
  if (eqsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
351
- }
503
+ }; break;
504
+ case 'vector':
505
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/)))
506
+ return {...p2, fields: ['vector-member']};
352
507
  }
353
508
  throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
354
509
  };
@@ -356,10 +511,11 @@ class TypePatternGraph {
356
511
  break;
357
512
  case '[]':
358
513
  switch (p2.pattern) {
359
- case '[]':
360
- if (eqsetP(p1.fields, p2.fields)) return p2;
361
- throw new Error(`Cannot unify [${p1.fields}] with [${p2.fields}]`);
362
514
  case '{}':
515
+ // if (p2.fields.every(x => `${x}`.match(/^[0-9]+$/)))
516
+ // return p1;
517
+ throw new Error(`Cannot unify [] with {${p2.fields}}`);
518
+ case '[]':
363
519
  return p1;
364
520
  case '<>':
365
521
  throw new Error('Cannot unify [] with <>');
@@ -435,8 +591,13 @@ class TypePatternGraph {
435
591
  const fields = Object.keys(this.edges[x] || {});
436
592
  return {...pattern, fields: fields};
437
593
  });
438
- const new_pattern = {...this.unify_patterns(...rep_patters), rule : rule};
439
- // console.log(` new pattern: ${JSON.stringify(new_pattern)}`);
594
+
595
+ const new_pattern = (() => { try {
596
+ return {...this.unify_patterns(...rep_patters), rule : rule}
597
+ } catch (e) {
598
+ e.message = `Unification rule "${rule}"\n - ${e.message}`;
599
+ throw e;
600
+ } })();
440
601
  const new_id = (() => {
441
602
  if (new_pattern.pattern == 'type') {
442
603
  const id = this.getTypeId(new_pattern.type);
@@ -524,5 +685,5 @@ class TypePatternGraph {
524
685
  }
525
686
  }
526
687
 
527
- export {TypePatternForest, TypePatternGraph};
528
- export default {TypePatternForest, TypePatternGraph};
688
+ export { TypePatternGraph };
689
+ export default { TypePatternGraph };
package/INTRO.md DELETED
@@ -1,88 +0,0 @@
1
- ### Introduction to Kernel/Core
2
-
3
- **Kernel/Core** is a minimal, gradually typed, first-order functional programming language designed for defining and transforming data using partial functions. It is built on the foundation of Algebraic Data Types (ADTs), specifically tagged unions and products.
4
-
5
- #### Core Concepts
6
-
7
- 1. **Statements**:
8
- - The program consists of a possibly empty list of statements, each ending with a semicolon (`;`), followed by an expression.
9
- - There are two types of statements:
10
- - **Code Definitions**: Define data types and start with a dollar sign (`$`).
11
- - **Relation Definitions**: Define partial functions (transformations) of data.
12
-
13
- 2. **Codes**:
14
- - Codes represent data types and can be either unions (`<...>`) or products (`{...}`).
15
- - Example:
16
- ```kernelcore
17
- $ bool = <{} true, {} false>;
18
- $ pair_of_bools = {bool x, bool y};
19
- ```
20
-
21
- 3. **Relations**:
22
- - Relations are partial functions defined to transform data from one type to another.
23
- - Example:
24
- ```kernelcore
25
- is_x_true = $pair_of_bools (.x .true);
26
- ```
27
-
28
- 4. **Composition of Partial Functions**:
29
- - **Sequential (Functional) Composition**: Sequential application of partial functions.
30
- ```kernelcore
31
- (.x .true)
32
- ```
33
- - **Parallel (Product) Composition**: Combine multiple partial functions to construct a new product type.
34
- ```kernelcore
35
- {.x x_value, .y y_value}
36
- ```
37
- - **Concurrent (Union) Composition**: Try different functions in sequence, returning the first successful result.
38
- ```kernelcore
39
- <{.x .true false_bool}, {} true_bool>
40
- ```
41
-
42
- 5. **Constants**:
43
- - Constants are defined as partial functions that ignore their input.
44
- - Example:
45
- ```kernelcore
46
- true_bool = {{} true} $bool;
47
- false_bool = {{} false} $bool;
48
- ```
49
-
50
- #### Example Program
51
-
52
- ```kernelcore
53
- // Define a boolean type as a union of true and false
54
- $ bool = <{} true, {} false>;
55
-
56
- // Define a pair of booleans as a product of two booleans
57
- $ pair_of_bools = {bool x, bool y};
58
-
59
- // Check if the `x` field is `true`
60
- is_x_true = $pair_of_bools (.x .true);
61
-
62
- // Extract both `x` and `y` fields into a new product
63
- extract_both = $pair_of_bools {.x x_value, .y y_value} $pair_of_bools;
64
-
65
- // Check if either `x` or `y` is `true`
66
- is_any_true = $pair_of_bools <{.x .true false_bool}, {} true_bool> $bool;
67
-
68
- // Define constants
69
- true_bool = {{} true} $bool;
70
- false_bool = {{} false} $bool;
71
-
72
- // Create instances of `pair_of_bools`
73
- pair1 = {true_bool x, true_bool y} $pair_of_bools;
74
- pair2 = {true_bool x, false_bool y} $pair_of_bools;
75
- pair3 = {false_bool x, false_bool y} $pair_of_bools;
76
-
77
- // Example usage of the functions
78
- result1 = pair1 is_x_true; // Should return true
79
- result2 = pair2 is_x_true; // Should return true
80
-
81
- both_values = pair1 extract_both; // Should return {x_value true, y_value true}
82
-
83
- any_true1 = pair1 is_any_true; // Should return true
84
- any_true2 = pair2 is_any_true; // Should return true
85
- any_true3 = pair3 is_any_true; // Should return false
86
- ```
87
-
88
- This brief introduction covers the fundamental concepts of Kernel/Core, enabling you to start defining data types and writing partial functions to transform data.