@elaraai/east 0.0.1-beta.0 → 0.0.1-beta.2

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.
Files changed (101) hide show
  1. package/README.md +39 -10
  2. package/dist/src/analyze.d.ts +4 -4
  3. package/dist/src/analyze.d.ts.map +1 -1
  4. package/dist/src/analyze.js +142 -44
  5. package/dist/src/analyze.js.map +1 -1
  6. package/dist/src/ast.d.ts +18 -7
  7. package/dist/src/ast.d.ts.map +1 -1
  8. package/dist/src/ast.js +1 -146
  9. package/dist/src/ast.js.map +1 -1
  10. package/dist/src/ast_to_ir.d.ts +1 -0
  11. package/dist/src/ast_to_ir.d.ts.map +1 -1
  12. package/dist/src/ast_to_ir.js +71 -10
  13. package/dist/src/ast_to_ir.js.map +1 -1
  14. package/dist/src/builtins.d.ts.map +1 -1
  15. package/dist/src/builtins.js +79 -64
  16. package/dist/src/builtins.js.map +1 -1
  17. package/dist/src/comparison.d.ts.map +1 -1
  18. package/dist/src/comparison.js +21 -0
  19. package/dist/src/comparison.js.map +1 -1
  20. package/dist/src/compile.d.ts.map +1 -1
  21. package/dist/src/compile.js +114 -220
  22. package/dist/src/compile.js.map +1 -1
  23. package/dist/src/default.d.ts.map +1 -1
  24. package/dist/src/default.js +2 -0
  25. package/dist/src/default.js.map +1 -1
  26. package/dist/src/eastir.d.ts +12 -10
  27. package/dist/src/eastir.d.ts.map +1 -1
  28. package/dist/src/eastir.js +20 -19
  29. package/dist/src/eastir.js.map +1 -1
  30. package/dist/src/expr/array.js +51 -51
  31. package/dist/src/expr/array.js.map +1 -1
  32. package/dist/src/expr/ast.d.ts.map +1 -1
  33. package/dist/src/expr/ast.js +6 -0
  34. package/dist/src/expr/ast.js.map +1 -1
  35. package/dist/src/expr/asyncfunction.d.ts +49 -0
  36. package/dist/src/expr/asyncfunction.d.ts.map +1 -0
  37. package/dist/src/expr/asyncfunction.js +60 -0
  38. package/dist/src/expr/asyncfunction.js.map +1 -0
  39. package/dist/src/expr/block.d.ts +60 -6
  40. package/dist/src/expr/block.d.ts.map +1 -1
  41. package/dist/src/expr/block.js +251 -14
  42. package/dist/src/expr/block.js.map +1 -1
  43. package/dist/src/expr/dict.js +47 -47
  44. package/dist/src/expr/dict.js.map +1 -1
  45. package/dist/src/expr/expr.d.ts +9 -0
  46. package/dist/src/expr/expr.d.ts.map +1 -1
  47. package/dist/src/expr/expr.js +5 -1
  48. package/dist/src/expr/expr.js.map +1 -1
  49. package/dist/src/expr/index.d.ts +73 -2
  50. package/dist/src/expr/index.d.ts.map +1 -1
  51. package/dist/src/expr/index.js +72 -8
  52. package/dist/src/expr/index.js.map +1 -1
  53. package/dist/src/expr/libs/array.js +1 -1
  54. package/dist/src/expr/libs/array.js.map +1 -1
  55. package/dist/src/expr/libs/dict.js +3 -3
  56. package/dist/src/expr/libs/dict.js.map +1 -1
  57. package/dist/src/expr/libs/set.js +2 -2
  58. package/dist/src/expr/libs/set.js.map +1 -1
  59. package/dist/src/expr/ref.js +1 -1
  60. package/dist/src/expr/ref.js.map +1 -1
  61. package/dist/src/expr/set.js +38 -38
  62. package/dist/src/expr/set.js.map +1 -1
  63. package/dist/src/expr/struct.d.ts +2 -0
  64. package/dist/src/expr/struct.d.ts.map +1 -1
  65. package/dist/src/expr/types.d.ts +8 -5
  66. package/dist/src/expr/types.d.ts.map +1 -1
  67. package/dist/src/fuzz.d.ts.map +1 -1
  68. package/dist/src/fuzz.js +5 -0
  69. package/dist/src/fuzz.js.map +1 -1
  70. package/dist/src/internal.d.ts +11 -9
  71. package/dist/src/internal.d.ts.map +1 -1
  72. package/dist/src/internal.js +15 -5
  73. package/dist/src/internal.js.map +1 -1
  74. package/dist/src/ir.d.ts +231 -33
  75. package/dist/src/ir.d.ts.map +1 -1
  76. package/dist/src/ir.js +3 -1
  77. package/dist/src/ir.js.map +1 -1
  78. package/dist/src/serialization/beast.d.ts.map +1 -1
  79. package/dist/src/serialization/beast.js +6 -0
  80. package/dist/src/serialization/beast.js.map +1 -1
  81. package/dist/src/serialization/beast2-stream.d.ts.map +1 -1
  82. package/dist/src/serialization/beast2-stream.js +5 -0
  83. package/dist/src/serialization/beast2-stream.js.map +1 -1
  84. package/dist/src/serialization/beast2.d.ts.map +1 -1
  85. package/dist/src/serialization/beast2.js +6 -0
  86. package/dist/src/serialization/beast2.js.map +1 -1
  87. package/dist/src/serialization/east.d.ts.map +1 -1
  88. package/dist/src/serialization/east.js +9 -1
  89. package/dist/src/serialization/east.js.map +1 -1
  90. package/dist/src/serialization/json.d.ts.map +1 -1
  91. package/dist/src/serialization/json.js +8 -2
  92. package/dist/src/serialization/json.js.map +1 -1
  93. package/dist/src/type_of_type.d.ts +19 -32
  94. package/dist/src/type_of_type.d.ts.map +1 -1
  95. package/dist/src/type_of_type.js +126 -14
  96. package/dist/src/type_of_type.js.map +1 -1
  97. package/dist/src/types.d.ts +59 -44
  98. package/dist/src/types.d.ts.map +1 -1
  99. package/dist/src/types.js +191 -91
  100. package/dist/src/types.js.map +1 -1
  101. package/package.json +3 -3
package/dist/src/types.js CHANGED
@@ -48,9 +48,6 @@ export const BlobType = { type: "Blob" };
48
48
  * @throws When the element type contains functions
49
49
  */
50
50
  export function RefType(type) {
51
- if (typeof type !== "string" && !isDataType(type)) {
52
- throw new Error(`Ref value type must be a (non-function) data type, got ${printType(type)}`);
53
- }
54
51
  return { type: "Ref", value: type };
55
52
  }
56
53
  ;
@@ -63,9 +60,6 @@ export function RefType(type) {
63
60
  * @throws When the element type contains functions
64
61
  */
65
62
  export function ArrayType(type) {
66
- if (typeof type !== "string" && !isDataType(type)) {
67
- throw new Error(`Array value type must be a (non-function) data type, got ${printType(type)}`);
68
- }
69
63
  return { type: "Array", value: type };
70
64
  }
71
65
  ;
@@ -98,9 +92,6 @@ export function DictType(key, value) {
98
92
  if (typeof key !== "string" && !isImmutableType(key)) {
99
93
  throw new Error(`Dict key type must be an immutable type, got ${printType(key)}`);
100
94
  }
101
- if (typeof value !== "string" && !isDataType(value)) {
102
- throw new Error(`Dict value type must be a (non-function) data type, got ${printType(value)}`);
103
- }
104
95
  return { type: "Dict", key, value };
105
96
  }
106
97
  ;
@@ -112,12 +103,6 @@ export function DictType(key, value) {
112
103
  * @returns A Struct type
113
104
  */
114
105
  export function StructType(field_types) {
115
- // Validate all field types are data types (no functions)
116
- for (const [field_name, field_type] of Object.entries(field_types)) {
117
- if (typeof field_type !== "string" && !isDataType(field_type)) {
118
- throw new Error(`Struct field ${field_name} must be a (non-function) data type, got ${printType(field_type)}`);
119
- }
120
- }
121
106
  return { type: "Struct", fields: field_types };
122
107
  }
123
108
  ;
@@ -129,12 +114,6 @@ export function StructType(field_types) {
129
114
  * @returns A Variant type with cases sorted alphabetically
130
115
  */
131
116
  export function VariantType(case_types) {
132
- // Validate all case types are data types (no functions)
133
- for (const [case_name, case_type] of Object.entries(case_types)) {
134
- if (typeof case_type !== "string" && !isDataType(case_type)) {
135
- throw new Error(`Variant case ${case_name} must be a (non-function) data type, got ${printType(case_type)}`);
136
- }
137
- }
138
117
  // Cases are sorted alphabetically by their name
139
118
  const cases_sorted = Object.fromEntries(Object.entries(case_types).sort((x, y) => x[0] < y[0] ? -1 : x[0] === y[0] ? 0 : 1));
140
119
  return { type: "Variant", cases: cases_sorted };
@@ -154,7 +133,7 @@ export function VariantType(case_types) {
154
133
  */
155
134
  function validateNotMutuallyRecursive(type, allowedMarker) {
156
135
  const visited = new Set();
157
- function check(t) {
136
+ function check(t, allowed) {
158
137
  // Skip string placeholders (used for generic builtins)
159
138
  if (typeof t === "string")
160
139
  return;
@@ -167,8 +146,12 @@ function validateNotMutuallyRecursive(type, allowedMarker) {
167
146
  throw new Error(`Invalid type encountered during recursion validation: ${t}${allowedMarkerString === null ? "" : ` (allowed marker: ${allowedMarkerString})`}`);
168
147
  }
169
148
  // Skip allowedMarker
170
- if (t === allowedMarker)
149
+ if (t === allowedMarker) {
150
+ if (!allowed) {
151
+ throw new Error("RecursiveType cannot pass into set keys, dictionary keys, or function input/output types");
152
+ }
171
153
  return;
154
+ }
172
155
  // Avoid infinite loops
173
156
  if (visited.has(t))
174
157
  return;
@@ -185,31 +168,38 @@ function validateNotMutuallyRecursive(type, allowedMarker) {
185
168
  }
186
169
  else if (t.type === "Struct") {
187
170
  for (const [_, field_type] of Object.entries(t.fields)) {
188
- check(field_type);
171
+ check(field_type, allowed);
189
172
  }
190
173
  }
191
174
  else if (t.type === "Variant") {
192
175
  for (const [_, case_type] of Object.entries(t.cases)) {
193
- check(case_type);
176
+ check(case_type, allowed);
194
177
  }
195
178
  }
179
+ else if (t.type === "Ref") {
180
+ check(t.value, allowed);
181
+ }
196
182
  else if (t.type === "Array") {
197
- check(t.value);
183
+ check(t.value, allowed);
198
184
  }
199
185
  else if (t.type === "Set") {
200
- check(t.key);
186
+ check(t.key, false);
201
187
  }
202
188
  else if (t.type === "Dict") {
203
- check(t.key);
204
- check(t.value);
189
+ check(t.key, false);
190
+ check(t.value, allowed);
205
191
  }
206
192
  else if (t.type === "Function") {
207
- t.inputs.forEach(check);
208
- check(t.output);
193
+ t.inputs.forEach(input => check(input, false));
194
+ check(t.output, false);
195
+ }
196
+ else if (t.type === "AsyncFunction") {
197
+ t.inputs.forEach(input => check(input, false));
198
+ check(t.output, false);
209
199
  }
210
200
  // Primitive types don't need recursion
211
201
  }
212
- check(type);
202
+ check(type, true);
213
203
  }
214
204
  /**
215
205
  * Constructs a recursive type with the specified node structure.
@@ -252,25 +242,33 @@ export function RecursiveType(f) {
252
242
  ret.node = type;
253
243
  // Validate SCC size 1 (no nested RecursiveTypes with cross-references)
254
244
  validateNotMutuallyRecursive(type, ret);
255
- if (typeof type !== "string" && !isDataType(type)) {
256
- throw new Error(`Recursive node type must be a (non-function) data type, got ${printType(type)}`);
257
- }
258
245
  return ret;
259
246
  }
260
247
  ;
261
248
  /**
262
249
  * Constructs a Function type with the specified input and output types.
263
250
  *
264
- * @typeParam I - Tuple type of input parameter types
265
- * @typeParam O - The output/return type
266
251
  * @param inputs - Array of {@link EastType} instances for each parameter
267
252
  * @param output - The {@link EastType} of the return value
268
253
  * @returns A Function type
254
+ *
255
+ * @see {@link AsyncFunctionType} for asynchronous functions.
269
256
  */
270
- export function FunctionType(inputs, output, platforms) {
271
- // TODO - for the moment we default function types to being pure, meaning users may need to annotate their higher-order functions.
272
- // In future we may want to track side-effects more precisely.
273
- return { type: "Function", inputs, output, platforms };
257
+ export function FunctionType(inputs, output) {
258
+ return { type: "Function", inputs, output };
259
+ }
260
+ ;
261
+ /**
262
+ * Constructs an AsyncFunction type with the specified input and output types.
263
+ *
264
+ * @param inputs - Array of {@link EastType} instances for each parameter
265
+ * @param output - The {@link EastType} of the return value
266
+ * @returns An AsyncFunction type
267
+ *
268
+ * @see {@link AsyncFunctionType} for asynchronous functions.
269
+ */
270
+ export function AsyncFunctionType(inputs, output) {
271
+ return { type: "AsyncFunction", inputs, output };
274
272
  }
275
273
  ;
276
274
  /**
@@ -281,8 +279,7 @@ export function FunctionType(inputs, output, platforms) {
281
279
  * @returns `true` if the type is a pure data type, `false` if it contains functions
282
280
  *
283
281
  * @remarks
284
- * Data types can be fully serialized and transmitted between runtimes.
285
- * This recursively checks {@link StructType}, {@link VariantType}, and {@link RecursiveType} fields.
282
+ * Data types have a total ordering, can be fully serialized and transmitted between runtimes.
286
283
  */
287
284
  export function isDataType(type, recursive_type) {
288
285
  // Avoid infinite loops
@@ -290,20 +287,18 @@ export function isDataType(type, recursive_type) {
290
287
  return true;
291
288
  }
292
289
  if (type.type === "Ref") {
293
- // Ref constructors check their value type are data types
294
- return true;
290
+ return isDataType(type.value, recursive_type);
295
291
  }
296
292
  else if (type.type === "Array") {
297
- // Array constructors check their value type are data types
298
- return true;
293
+ return isDataType(type.value, recursive_type);
299
294
  }
300
295
  else if (type.type === "Set") {
301
- // Set constructors check their value type are (immutable) data types
296
+ // Set constructors check their key type is an (immutable) data type
302
297
  return true;
303
298
  }
304
299
  else if (type.type === "Dict") {
305
- // Dict constructors check their key and value types are data types
306
- return true;
300
+ // Dict constructors check their key type is an (immutable) data type
301
+ return isDataType(type.value, recursive_type);
307
302
  }
308
303
  else if (type.type === "Struct") {
309
304
  for (const field_type of Object.values(type.fields)) {
@@ -327,6 +322,9 @@ export function isDataType(type, recursive_type) {
327
322
  else if (type.type === "Function") {
328
323
  return false;
329
324
  }
325
+ else if (type.type === "AsyncFunction") {
326
+ return false;
327
+ }
330
328
  else {
331
329
  // Primitive types are data types
332
330
  return true;
@@ -340,8 +338,7 @@ export function isDataType(type, recursive_type) {
340
338
  * @returns `true` if the type is immutable, `false` if it contains mutable collections or functions
341
339
  *
342
340
  * @remarks
343
- * Immutable types exclude {@link ArrayType}, {@link SetType}, {@link DictType}, and {@link FunctionType}.
344
- * This recursively checks {@link StructType}, {@link VariantType}, and {@link RecursiveType} fields.
341
+ * Immutable types exclude {@link ArrayType}, {@link SetType}, {@link DictType}, {@link FunctionType} and {@link AsyncFunctionType}.
345
342
  */
346
343
  export function isImmutableType(type, recursive_type) {
347
344
  // Avoid infinite loops
@@ -387,6 +384,10 @@ export function isImmutableType(type, recursive_type) {
387
384
  // Functions are not data types, and "immutable data" is a subset of "data"
388
385
  return false;
389
386
  }
387
+ else if (type.type === "AsyncFunction") {
388
+ // Functions are not data types, and "immutable data" is a subset of "data"
389
+ return false;
390
+ }
390
391
  else {
391
392
  // Primitive types are immutable
392
393
  return true;
@@ -470,7 +471,34 @@ export function EastTypeOf(value) {
470
471
  * For {@link StructType} and {@link VariantType}, field/case order matters.
471
472
  * For {@link RecursiveType}, uses cycle tracking to handle recursive references.
472
473
  */
474
+ // Cache for memoizing isTypeEqual results (top-level calls only)
475
+ const typeEqualCache = new WeakMap();
473
476
  export function isTypeEqual(t1, t2, r1 = t1, r2 = t2) {
477
+ // Fast path: reference equality
478
+ if (t1 === t2)
479
+ return true;
480
+ // Memoization for top-level calls only (r1/r2 are just for internal cycle detection)
481
+ const isTopLevel = r1 === t1 && r2 === t2;
482
+ if (isTopLevel) {
483
+ const innerCache = typeEqualCache.get(t1);
484
+ if (innerCache) {
485
+ const cached = innerCache.get(t2);
486
+ if (cached !== undefined)
487
+ return cached;
488
+ }
489
+ const result = isTypeEqualImpl(t1, t2, r1, r2);
490
+ // Store result in cache
491
+ let cache = typeEqualCache.get(t1);
492
+ if (!cache) {
493
+ cache = new WeakMap();
494
+ typeEqualCache.set(t1, cache);
495
+ }
496
+ cache.set(t2, result);
497
+ return result;
498
+ }
499
+ return isTypeEqualImpl(t1, t2, r1, r2);
500
+ }
501
+ function isTypeEqualImpl(t1, t2, r1, r2) {
474
502
  if (t1.type === "Recursive") {
475
503
  if (t2.type === "Recursive") {
476
504
  if (t1.node === r1) {
@@ -607,21 +635,25 @@ export function isTypeEqual(t1, t2, r1 = t1, r2 = t2) {
607
635
  if (!isTypeEqual(t1.output, t2.output, r1, r2)) {
608
636
  return false;
609
637
  }
610
- // Check platform requirements match
611
- if (t1.platforms === null && t2.platforms === null) {
612
- return true;
613
- }
614
- if (t1.platforms === null || t2.platforms === null) {
638
+ return true;
639
+ }
640
+ else if (t1.type === "AsyncFunction") {
641
+ if (t2.type !== "AsyncFunction") {
615
642
  return false;
616
643
  }
617
- if (t1.platforms.length !== t2.platforms.length) {
644
+ // Check input types match
645
+ if (t1.inputs.length !== t2.inputs.length) {
618
646
  return false;
619
647
  }
620
- for (let i = 0; i < t1.platforms.length; i++) {
621
- if (t1.platforms[i] !== t2.platforms[i]) {
648
+ for (let i = 0; i < t1.inputs.length; i++) {
649
+ if (!isTypeEqual(t1.inputs[i], t2.inputs[i], r1, r2)) {
622
650
  return false;
623
651
  }
624
652
  }
653
+ // Check output type matches
654
+ if (!isTypeEqual(t1.output, t2.output, r1, r2)) {
655
+ return false;
656
+ }
625
657
  return true;
626
658
  }
627
659
  else {
@@ -750,6 +782,9 @@ export function isValueOf(value, type, node_type, nodes_visited) {
750
782
  else if (type.type === "Function") {
751
783
  throw new Error('Javascript functions cannot be converted to East functions');
752
784
  }
785
+ else if (type.type === "AsyncFunction") {
786
+ throw new Error('Javascript functions cannot be converted to East async functions');
787
+ }
753
788
  else {
754
789
  throw new Error(`Unknown type encountered during value type check: ${type.type}`);
755
790
  }
@@ -843,7 +878,14 @@ export function printType(type, stack = []) {
843
878
  else if (type.type === "Function") {
844
879
  // Note: functions can't be inside recursive types
845
880
  stack.push(type);
846
- const ret = `.Function (inputs=[${type.inputs.map(t => printType(t, stack)).join(", ")}], output=${printType(type.output, stack)}, platforms=${type.platforms === null ? "null" : `[${type.platforms.map(p => JSON.stringify(p)).join(", ")}]`})`;
881
+ const ret = `.Function (inputs=[${type.inputs.map(t => printType(t, stack)).join(", ")}], output=${printType(type.output, stack)})`;
882
+ stack.pop();
883
+ return ret;
884
+ }
885
+ else if (type.type === "AsyncFunction") {
886
+ // Note: functions can't be inside recursive types
887
+ stack.push(type);
888
+ const ret = `.AsyncFunction (inputs=[${type.inputs.map(t => printType(t, stack)).join(", ")}], output=${printType(type.output, stack)})`;
847
889
  stack.pop();
848
890
  return ret;
849
891
  }
@@ -865,7 +907,30 @@ export function printType(type, stack = []) {
865
907
  * - {@link VariantType} supports width subtyping (more cases → fewer cases)
866
908
  * - {@link FunctionType} uses contravariant inputs and covariant outputs
867
909
  */
910
+ // Cache for memoizing isSubtype results
911
+ const isSubtypeCache = new WeakMap();
868
912
  export function isSubtype(t1, t2) {
913
+ // Fast path: reference equality (reflexivity)
914
+ if (t1 === t2)
915
+ return true;
916
+ // Check cache
917
+ const innerCache = isSubtypeCache.get(t1);
918
+ if (innerCache) {
919
+ const cached = innerCache.get(t2);
920
+ if (cached !== undefined)
921
+ return cached;
922
+ }
923
+ const result = isSubtypeImpl(t1, t2);
924
+ // Store in cache
925
+ let cache = isSubtypeCache.get(t1);
926
+ if (!cache) {
927
+ cache = new WeakMap();
928
+ isSubtypeCache.set(t1, cache);
929
+ }
930
+ cache.set(t2, result);
931
+ return result;
932
+ }
933
+ function isSubtypeImpl(t1, t2) {
869
934
  // Equi-recursive type subtyping: unfold recursive types when only one side is recursive
870
935
  if (t1.type === "Recursive") {
871
936
  if (t2.type === "Recursive") {
@@ -975,7 +1040,15 @@ export function isSubtype(t1, t2) {
975
1040
  }
976
1041
  }
977
1042
  else if (t1.type === "Function") {
978
- if (t2.type === "Function") {
1043
+ if (t2.type === "Function" || t2.type === "AsyncFunction") {
1044
+ return t1.inputs.length === t2.inputs.length && t1.inputs.every((t, i) => isSubtype(t2.inputs[i], t)) && isSubtype(t1.output, t2.output); // contravariant inputs and covariant output
1045
+ }
1046
+ else {
1047
+ return false;
1048
+ }
1049
+ }
1050
+ else if (t1.type === "AsyncFunction") {
1051
+ if (t2.type === "AsyncFunction") {
979
1052
  return t1.inputs.length === t2.inputs.length && t1.inputs.every((t, i) => isSubtype(t2.inputs[i], t)) && isSubtype(t1.output, t2.output); // contravariant inputs and covariant output
980
1053
  }
981
1054
  else {
@@ -1112,17 +1185,30 @@ export function TypeUnion(t1, t2) {
1112
1185
  if (t1.inputs.length !== t2.inputs.length) {
1113
1186
  throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1114
1187
  }
1115
- let platforms;
1116
- if (t1.platforms === null) {
1117
- platforms = null;
1188
+ return FunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
1189
+ }
1190
+ else if (t2.type === "AsyncFunction") {
1191
+ if (t1.inputs.length !== t2.inputs.length) {
1192
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1118
1193
  }
1119
- else if (t2.platforms === null) {
1120
- platforms = null;
1194
+ return AsyncFunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
1195
+ }
1196
+ else {
1197
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
1198
+ }
1199
+ }
1200
+ else if (t1.type === "AsyncFunction") {
1201
+ if (t2.type === "Function") {
1202
+ if (t1.inputs.length !== t2.inputs.length) {
1203
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1121
1204
  }
1122
- else {
1123
- platforms = [...new Set([...t1.platforms, ...t2.platforms])].sort();
1205
+ return AsyncFunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
1206
+ }
1207
+ else if (t2.type === "AsyncFunction") {
1208
+ if (t1.inputs.length !== t2.inputs.length) {
1209
+ throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1124
1210
  }
1125
- return FunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output), platforms);
1211
+ return AsyncFunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
1126
1212
  }
1127
1213
  else {
1128
1214
  throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
@@ -1265,17 +1351,30 @@ export function TypeIntersect(t1, t2) {
1265
1351
  if (t1.inputs.length !== t2.inputs.length) {
1266
1352
  throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1267
1353
  }
1268
- let platforms;
1269
- if (t1.platforms === null) {
1270
- platforms = t2.platforms;
1354
+ return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
1355
+ }
1356
+ else if (t2.type === "AsyncFunction") {
1357
+ if (t1.inputs.length !== t2.inputs.length) {
1358
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1271
1359
  }
1272
- else if (t2.platforms === null) {
1273
- platforms = t1.platforms;
1360
+ return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
1361
+ }
1362
+ else {
1363
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
1364
+ }
1365
+ }
1366
+ else if (t1.type === "AsyncFunction") {
1367
+ if (t2.type === "Function") {
1368
+ if (t1.inputs.length !== t2.inputs.length) {
1369
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1274
1370
  }
1275
- else {
1276
- platforms = t1.platforms.filter(p => t2.platforms.includes(p));
1371
+ return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
1372
+ }
1373
+ else if (t2.type === "AsyncFunction") {
1374
+ if (t1.inputs.length !== t2.inputs.length) {
1375
+ throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
1277
1376
  }
1278
- return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output), platforms);
1377
+ return AsyncFunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
1279
1378
  }
1280
1379
  else {
1281
1380
  throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
@@ -1423,20 +1522,18 @@ export function TypeEqual(t1, t2, r1 = t1, r2 = t2) {
1423
1522
  if (t1.inputs.length !== t2.inputs.length) {
1424
1523
  throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions take different number of arguments`);
1425
1524
  }
1426
- if (t1.platforms === null) {
1427
- if (t2.platforms !== null) {
1428
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions have different platform effects`);
1429
- }
1430
- }
1431
- else if (t2.platforms === null) {
1432
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions have different platform effects`);
1433
- }
1434
- else {
1435
- if (t1.platforms.length !== t2.platforms.length || !t1.platforms.every((p, i) => p === t2.platforms[i])) {
1436
- throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions have different platform effects`);
1437
- }
1525
+ return FunctionType(t1.inputs.map((t, i) => TypeEqual(t, t2.inputs[i], r1, r2)), TypeEqual(t1.output, t2.output, r1, r2));
1526
+ }
1527
+ else {
1528
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
1529
+ }
1530
+ }
1531
+ else if (t1.type === "AsyncFunction") {
1532
+ if (t2.type === "AsyncFunction") {
1533
+ if (t1.inputs.length !== t2.inputs.length) {
1534
+ throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions take different number of arguments`);
1438
1535
  }
1439
- return FunctionType(t1.inputs.map((t, i) => TypeEqual(t, t2.inputs[i], r1, r2)), TypeEqual(t1.output, t2.output, r1, r2), t1.platforms);
1536
+ return AsyncFunctionType(t1.inputs.map((t, i) => TypeEqual(t, t2.inputs[i], r1, r2)), TypeEqual(t1.output, t2.output, r1, r2));
1440
1537
  }
1441
1538
  else {
1442
1539
  throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
@@ -1565,6 +1662,9 @@ export function TypeWiden(t1, t2) {
1565
1662
  else if (t1.type === "Function") {
1566
1663
  throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: functions not supported`);
1567
1664
  }
1665
+ else if (t1.type === "AsyncFunction") {
1666
+ throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: functions not supported`);
1667
+ }
1568
1668
  else {
1569
1669
  if (t1.type === t2.type) {
1570
1670
  return t1;