kopscript 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LLM.md CHANGED
@@ -133,8 +133,8 @@ Dog d = new Dog("Rex");
133
133
  the interface in the base list and provide matching public methods, checked at compile time.
134
134
  - No nested classes/functions.
135
135
  - No multiple inheritance (at most one class in the base list; the rest must be interfaces).
136
- - `class Box<T> { ... }` a single type parameter is allowed; see Generics below for the
137
- full (deliberately scoped-down) rules.
136
+ - `class Box<T> { ... }` / `class Pair<K, V> { ... }` one or more type parameters are
137
+ allowed; see Generics below for the full (deliberately scoped-down) rules.
138
138
 
139
139
  ### Interfaces
140
140
 
@@ -145,7 +145,7 @@ interface IShape {
145
145
  interface INamedShape : IShape { // interfaces can extend other interfaces (not classes)
146
146
  string GetName();
147
147
  }
148
- interface IContainer<T> { // interfaces take a single type parameter too — see Generics
148
+ interface IContainer<T> { // interfaces take type parameters too — see Generics
149
149
  T Get();
150
150
  }
151
151
  ```
@@ -281,7 +281,7 @@ Not a use of user-declared generics (see below) — `state<T>` is one of exactly
281
281
  hardcoded parametrized forms in the type system (the other is `task<T>`), each with only
282
282
  ever one built-in meaning, unlike a real generic class/interface.
283
283
 
284
- ## Generics — a single type parameter on classes/interfaces
284
+ ## Generics — one or more type parameters on classes/interfaces
285
285
 
286
286
  ```ks
287
287
  class Box<T> {
@@ -293,38 +293,64 @@ class Box<T> {
293
293
  Box<number> nb = new Box<number>(5); // type argument required on both the type AND `new`
294
294
  Box<string> sb = new Box<string>("hi");
295
295
  print(nb.Get()); // 5
296
+
297
+ class Pair<K, V> {
298
+ public K First;
299
+ public V Second;
300
+ constructor(K k, V v) { this.First = k; this.Second = v; }
301
+ }
302
+
303
+ Pair<number, string> p = new Pair<number, string>(1, "a");
296
304
  ```
297
305
 
298
- One unconstrained, invariant type parameter per class/interface (real *or* `extern` — see
299
- the `extern` section below) the whole feature.
306
+ One or more unconstrained, invariant type parameters per class/interface (real *or*
307
+ `extern` — see the `extern` section below), comma-separated: `<T>`, `<K, V>`, `<A, B, C>`,
308
+ however many the declaration needs.
300
309
  Erased at codegen (JS has no generics either; `Box<number>` and `Box<string>` both
301
310
  compile to the exact same plain `class Box`), so there's no runtime cost and no way to
302
- inspect `T` at runtime.
311
+ inspect a type parameter at runtime.
303
312
 
304
313
  **Rules:**
305
- - The type argument is **required everywhere** a generic type is named — on the variable
306
- type (`Box<number>`) and separately on `new` (`new Box<number>(...)`); a bare `Box` (no
307
- argument) is a compile error, not "any"/inferred.
308
- - **Invariant**: `Box<Dog>` is not assignable to `Box<Animal>` even if `Dog : Animal` —
309
- type arguments must match exactly, not just be compatible.
310
- - Nesting works: `Box<Box<number>>`, `Box<number[]>`, `Box<number>?`, `Box<number>?[]`.
311
- - Inside the class/interface's own body, `T` is fully abstract you can store it, return
312
- it, pass it around, but (unconstrained) you **cannot call any member on a bare `T`
313
- value** (`this.value.Foo()` is a compile error: "Cannot access member 'Foo' on type
314
- 'T'"). This is correct, not a bug — same as an unconstrained type parameter in
315
- C#/Java/TypeScript.
314
+ - The type argument(s) are **required everywhere** a generic type is named — on the
315
+ variable type (`Box<number>`) and separately on `new` (`new Box<number>(...)`); a bare
316
+ `Box`/`Pair` (no arguments) is a compile error, not "any"/inferred. The count must match
317
+ the declaration exactly — `Pair<number>` (too few) and `Pair<number, string, bool>` (too
318
+ many) are both arity errors, not truncated/padded.
319
+ - **Invariant, per slot**: `Box<Dog>` is not assignable to `Box<Animal>` even if `Dog :
320
+ Animal`, and `Pair<number, string>` is not assignable to `Pair<string, number>` every
321
+ type argument must match exactly, in the same position, not just be compatible.
322
+ - Nesting works: `Box<Box<number>>`, `Box<number[]>`, `Box<number>?`, `Box<number>?[]`,
323
+ `Pair<number, Pair<string, bool>>`.
324
+ - Inside the class/interface's own body, each type parameter is fully abstract — you can
325
+ store it, return it, pass it around, but (unconstrained) you **cannot call any member on
326
+ a bare type-parameter value** (`this.value.Foo()` where `value: T` is a compile error:
327
+ "Cannot access member 'Foo' on type 'T'"). This is correct, not a bug — same as an
328
+ unconstrained type parameter in C#/Java/TypeScript.
329
+
330
+ **Generic inheritance**: a class/interface base list CAN name a generic base, with its own
331
+ type arguments, in either direction:
332
+ ```ks
333
+ class IntBox : Box<number> { } // concrete — threads a fixed type through
334
+ class Container<T> : Box<T> { // generic — threads its OWN T through
335
+ public T GetAgain() { return this.Get(); }
336
+ }
337
+ interface INumberContainer : IContainer<number> { } // interfaces work the same way
338
+ ```
339
+ Member resolution (fields, methods, an inherited constructor via `base(...)` or an implicit
340
+ default) composes substitutions correctly however many generic-base links up the chain a
341
+ member was actually declared — `Container<number>.Get()` (declared on `Box<T>`, two links
342
+ away in a longer chain) resolves to `number`, not the abstract `T`. **v1 limit**: at most
343
+ one generic entry across a class/interface's *whole* base list (its superclass, or one
344
+ implemented/extended interface — never more than one) — `class Foo<T> : Box<T>,
345
+ IContainer<T>` is a compile error ("has more than one generic entry in its base list") even
346
+ though either half alone would work.
316
347
 
317
348
  **Does not exist (v1 scope cuts, each deliberate)**:
318
349
  - **Generic functions.** `T Identity<T>(T x)` doesn't parse — only classes and interfaces
319
- take a type parameter, not free functions/methods themselves (a method *inside* a
320
- generic class can use that class's own `T` freely, same as any other member).
321
- - **Multiple type parameters.** No `Map<K, V>` — exactly one `<T>` or none.
350
+ take type parameters, not free functions/methods themselves (a method *inside* a
351
+ generic class can use that class's own type parameters freely, same as any other member).
322
352
  - **Constraints.** No `T : IComparable` — a type parameter is always fully unconstrained,
323
- which is why you can't call members on a bare `T` (see above).
324
- - **Generic inheritance.** A class/interface's base list can only name a *non-generic*
325
- type — `class Foo : Box<number>` and even `class Foo<T> : SomeGenericBase<T>` are both
326
- compile errors ("cannot extend/implement generic type '...' — not supported in v1"). A
327
- generic class/interface can still extend/implement ordinary non-generic bases normally.
353
+ which is why you can't call members on a bare one (see above).
328
354
  - **Variance.** No `out`/`in`/covariance/contravariance — see invariance above.
329
355
 
330
356
  ## Nullable types — `T?`
@@ -402,11 +428,13 @@ KopScript-declared name exactly. Extern class members use real JS member names v
402
428
  (camelCase, no rename mechanism). No inheritance modeling between two `extern class`
403
429
  declarations — each stands alone.
404
430
 
405
- `extern class` can carry `<T>` (`extern class Box<T> { constructor(T v); T Value { get; } }
406
- from "some-package";`) — identical rules to a real generic class (see "Generics" above:
407
- one unconstrained invariant parameter, erased, no base-list generics), so a generic type
408
- from another package instantiates and type-checks exactly like a local one
409
- (`Box<number>`, arity/invariance errors included).
431
+ `extern class` can carry type parameters (`extern class Box<T> { constructor(T v); T Value
432
+ { get; } } from "some-package";`) — identical rules to a real generic class (see "Generics"
433
+ above: one or more unconstrained invariant parameters, erased, generic inheritance
434
+ included), so a generic type from another package instantiates and type-checks exactly
435
+ like a local one (`Box<number>`, arity/invariance errors included) — and a real KopScript
436
+ class can extend a generic `extern class` with a concrete or threaded-through type
437
+ argument, same as extending a real generic base.
410
438
 
411
439
  **Never write `async` on an extern function/method signature** — declare its return type
412
440
  as `task`/`task<T>` directly (`task<string> text();`, not `async task<string> text();`).
@@ -493,9 +521,10 @@ Locals/params: **camelCase**. This is convention, not enforced by the compiler.
493
521
 
494
522
  ## Does not exist (don't reach for these)
495
523
 
496
- Generics beyond a single unconstrained type parameter (no `Map<K, V>`, no `T : IFoo`
497
- constraints, no generic functions, no generic inheritance, no variance see Generics
498
- above for what *is* supported) · `any`/`unknown` annotations ·
524
+ Generics beyond one or more unconstrained type parameters and single-generic-base
525
+ inheritance (no `T : IFoo` constraints, no generic functions, no variance, no more than one
526
+ generic entry per base list — see Generics above for what *is* supported) ·
527
+ `any`/`unknown` annotations ·
499
528
  a `let`/`var` keyword (locals are just `Type name = value;`) · decorators/annotations ·
500
529
  reflection · type inference on declarations · ternary expression · union/tuple types ·
501
530
  **object-literal syntax** (`{ key: value }` as a value — this doesn't exist *anywhere* in
package/README.md CHANGED
@@ -22,9 +22,9 @@ LLM's context, as opposed to this README's narrative explanation.
22
22
  explicit `virtual`/`override` dispatch (methods are sealed unless marked `virtual`), and a full
23
23
  `public`/`protected`/`private` access model enforced at compile time.
24
24
  - **Strongly typed**: every declaration is explicitly typed and checked at compile time.
25
- - **Generics**: a single, unconstrained, invariant type parameter on classes and
26
- interfaces (`class Box<T> { public T Value; }`) — erased at codegen with zero runtime
27
- cost, the same way `task<T>`/`state<T>` already are.
25
+ - **Generics**: one or more unconstrained, invariant type parameters on classes and
26
+ interfaces (`class Box<T> { public T Value; }`, `class Pair<K, V> { ... }`) — erased at
27
+ codegen with zero runtime cost, the same way `task<T>`/`state<T>` already are.
28
28
  - **Nullable types with compiler-enforced null-checking**: `T?` (`string?`, `Dog?`,
29
29
  `number[]?`) — a `T?` can't be used where a `T` is expected without an `if (x != null)`
30
30
  check first (checked statically, not just at runtime), and comparing a value that can
@@ -221,7 +221,7 @@ void Announce(ISpeaker s) {
221
221
 
222
222
  ### Generics
223
223
 
224
- Classes and interfaces can take a single type parameter:
224
+ Classes and interfaces can take one or more type parameters:
225
225
 
226
226
  ```ks
227
227
  class Box<T> {
@@ -233,34 +233,58 @@ class Box<T> {
233
233
  Box<number> nb = new Box<number>(5);
234
234
  Box<string> sb = new Box<string>("hi");
235
235
  print(nb.Get()); // 5
236
+
237
+ class Pair<K, V> {
238
+ public K First;
239
+ public V Second;
240
+ constructor(K k, V v) { this.First = k; this.Second = v; }
241
+ }
242
+
243
+ Pair<number, string> p = new Pair<number, string>(1, "a");
236
244
  ```
237
245
 
238
- The type argument is required everywhere the generic type is named — on the variable's
246
+ The type argument(s) are required everywhere the generic type is named — on the variable's
239
247
  declared type *and* separately on `new` (`Box<number> nb = new Box<number>(5);`, not
240
- just one or the other). Generics are erased at codegen exactly like `task<T>`/`state<T>`
241
- already are `Box<number>` and `Box<string>` compile to the identical plain `class Box`,
242
- so there's no runtime cost.
243
-
244
- This is deliberately the smallest useful slice of generics, not a scaled-down promise of
245
- more later inside v1:
246
-
247
- - **One type parameter, invariant, unconstrained.** No `Map<K, V>` (multiple parameters),
248
- no `T : ISomething` (constraints) — and because `T` is fully unconstrained, you can't
249
- call any member on a bare `T` value inside the generic class's own body (that's correct
250
- behavior, the same restriction C#/Java/TypeScript put on an unconstrained parameter, not
251
- a bug). Invariant means `Box<Dog>` is **not** assignable to `Box<Animal>` even though
252
- `Dog : Animal` type arguments must match exactly.
248
+ just one or the other) and the count must match the declaration exactly (`Pair<number>`
249
+ alone, or `Pair<number, string, bool>`, are both arity errors). Generics are erased at
250
+ codegen exactly like `task<T>`/`state<T>` already are — `Box<number>` and `Box<string>`
251
+ compile to the identical plain `class Box`, so there's no runtime cost.
252
+
253
+ Still a deliberately small slice of generics, not a scaled-down promise of more later
254
+ inside v1:
255
+
256
+ - **Invariant, unconstrained.** No `T : ISomething` (constraints) — and because a type
257
+ parameter is fully unconstrained, you can't call any member on a bare `T`/`K`/`V` value
258
+ inside the generic class's own body (that's correct behavior, the same restriction
259
+ C#/Java/TypeScript put on an unconstrained parameter, not a bug). Invariant means
260
+ `Box<Dog>` is **not** assignable to `Box<Animal>` even though `Dog : Animal`, and
261
+ `Pair<number, string>` is not assignable to `Pair<string, number>` — every slot must
262
+ match exactly, in order.
253
263
  - **No generic functions.** `T Identity<T>(T x)` isn't supported — only classes and
254
- interfaces take a type parameter. A method *inside* a generic class can still use that
255
- class's own `T` freely; it's just not introducing a type parameter of its own.
256
- - **No generic inheritance.** A class or interface's base list can only name a
257
- *non-generic* type. `class Foo : Box<number>` and `class Foo<T> : SomeBase<T>` are both
258
- compile errors — a generic class can still extend/implement ordinary non-generic bases
259
- normally, it just can't be the one on either side of a generic base relationship.
264
+ interfaces take type parameters. A method *inside* a generic class can still use that
265
+ class's own type parameters freely; it's just not introducing one of its own.
266
+
267
+ A class or interface *can* extend/implement a generic base, type arguments and all:
268
+
269
+ ```ks
270
+ class IntBox : Box<number> { } // concrete: threads a fixed type through
271
+ class Container<T> : Box<T> { // generic: threads its OWN T through
272
+ public T GetAgain() { return this.Get(); }
273
+ }
274
+ Container<number> c = new Container<number>(5);
275
+ print(c.GetAgain()); // 5, and c.Value/c.Get() work too
276
+ ```
277
+
278
+ Member lookup (fields, methods, an inherited constructor) resolves correctly however many
279
+ generic-base links up the chain a member was actually declared, substituting all the way
280
+ down. **v1 limit**: at most one generic entry across a whole base list (the superclass, or
281
+ one implemented interface — not several at once) — `class Foo<T> : Box<T>, IContainer<T>`
282
+ is a compile error even though each half would work alone.
260
283
 
261
284
  See `LLM.md`'s "Generics" section for the exhaustive rules if you're generating code
262
- against this — nested generics (`Box<Box<number>>`, `Box<number>?[]`) and generic
263
- interfaces used as standalone parameter types both work and are covered there.
285
+ against this — nested generics (`Box<Box<number>>`, `Box<number>?[]`, `Pair<number,
286
+ Pair<string, bool>>`) and generic interfaces used as standalone parameter types both work
287
+ and are covered there.
264
288
 
265
289
  ### Enums
266
290
 
@@ -359,11 +383,11 @@ consumes `Component`/`Router` themselves via `extern class ... from "kopular/...
359
383
  proving `extern` works as a real cross-*package* boundary, not just for describing DOM
360
384
  globals within a single project.
361
385
 
362
- An `extern class` can carry its own type parameter, `extern class Box<T> { ... }`
363
- exactly the same single-type-parameter rules as a real generic class (see "Generics"
364
- above: invariant, unconstrained, erased, can't appear in a base list), so a generic type
365
- from another package (e.g. Kopular's `FormField<T>`) can be described and instantiated
366
- generically, not just per concrete type:
386
+ An `extern class` can carry its own type parameter(s), `extern class Box<T> { ... }` or
387
+ `extern class Pair<K, V> { ... }` — exactly the same rules as a real generic class (see
388
+ "Generics" above: invariant, unconstrained, erased, and a real class can extend it as a
389
+ generic base), so a generic type from another package (e.g. Kopular's `FormField<T>`) can
390
+ be described and instantiated generically, not just per concrete type:
367
391
 
368
392
  ```ks
369
393
  extern class Box<T> {